Compartilhamento de superfície entre APIs de gráficos do Windows
Este tópico apresenta uma visão geral técnica da interoperabilidade usando o compartilhamento de superfície entre APIs gráficas do Windows, inclusive Direct3D 11, Direct2D, DirectWrite, Direct3D 10 e Direct3D 9Ex. Se já tiver um conhecimento prático dessas APIs, este documento poderá ajudar você a usar várias APIs para renderizar na mesma superfície em um aplicativo projetado para os sistemas operacionais Windows 7 ou Windows Vista. Este tópico também oferece diretrizes de melhores práticas e ponteiros para recursos adicionais.
Observação
Para interoperabilidade Direct2D e DirectWrite no runtime do DirectX 11.1, você pode usar dispositivos Direct2D e contextos de dispositivo para renderizar diretamente em dispositivos Direct3D 11.
Este tópico inclui as seções a seguir:
Introdução
Neste documento, a interoperabilidade da API de gráficos do Windows se refere ao compartilhamento da mesma superfície de renderização por APIs diferentes. Esse tipo de interoperabilidade permite que os aplicativos criem exibições atrativas aproveitando várias APIs gráficas do Windows e facilitem a migração para novas tecnologias, mantendo a compatibilidade com as APIs existentes.
No Windows 7 (e no Windows Vista SP2 com Windows 7 Interop Pack, Vista 7IP), as APIs de renderização de gráficos são Direct3D 11, Direct2D, Direct3D 10.1, Direct3D 10.0, Direct3D 9Ex, Direct3D 9c e APIs Direct3D anteriores, bem como GDI e GDI+. Windows Imaging Component (WIC) e DirectWrite são tecnologias relacionadas para processamento de imagens e Direct2D realiza a renderização de texto. A API da Aceleração de Vídeo DirectX (DXVA), baseada no Direct3D 9c e no Direct3D 9Ex, é usada em processamento de vídeo.
À medida que as APIs gráficas do Windows evoluem para se basearem em Direct3D, a Microsoft está investindo mais esforços para garantir a interoperabilidade entre as APIs. As APIs do Direct3D recém-desenvolvidas e as APIs de nível superior baseadas em APIs do Direct3D também dão suporte quando necessário para fazer a ponte de compatibilidade com APIs anteriores. Para ilustrar, os aplicativos Direct2D podem usar o Direct3D 10.1 compartilhando um dispositivo Direct3D 10.1. Além disso, as APIs Direct3D 11, Direct2D e Direct3D 10.1 podem aproveitar a DirectX Graphics Infrastructure (DXGI) 1.1, que permite superfícies compartilhadas sincronizadas que dão suporte total à interoperabilidade entre essas APIs. As APIs baseadas em DXGI 1.1 interoperam com GDI e com GDI+ por associação, obtendo o contexto do dispositivo GDI de uma superfície DXGI 1.1.
O compartilhamento de superfície não sincronizado é compatível com o tempo de execução do Direct3D 9Ex. Os aplicativos de vídeo baseados em DXVA podem usar o auxiliar de interoperabilidade Direct3D 9Ex e DXGI para interoperabilidade DXVA baseada em Direct3D 9Ex com Direct3D 11 para sombreador de computação ou podem interoperar com Direct2D para controles 2D ou renderização de texto. O WIC e o DirectWrite também interoperam com GDI, Direct2D e, por associação, com outras APIs do Direct3D.
Direct3D 10.0, Direct3D 9c e tempos de execução Direct3D anteriores não dão suporte a superfícies compartilhadas. As cópias de memória do sistema continuarão sendo usadas na interoperabilidade com APIs baseadas em GDI ou DXGI.
Os cenários de interoperabilidade neste documento se referem a várias APIs gráficas renderizadas em uma superfície de renderização compartilhada, e não na mesma janela do aplicativo. A sincronização de APIs à parte direcionadas a diferentes superfícies que são compostas na mesma janela está fora do escopo deste documento.
Visão geral sobre interoperabilidade da API
A interoperabilidade de compartilhamento de superfície de APIs gráficas do Windows pode ser descrita em termos de cenários de API para API e da funcionalidade de interoperabilidade correspondente. A partir do Windows 7 e a partir do Windows Vista SP2 com 7IP, as novas APIs e os tempos de execução associados incluem Direct2D e tecnologias relacionadas: Direct3D 11 e DXGI 1.1. O desempenho da GDI também foi melhorado no Windows 7. O Direct3D 10.1 foi introduzido no Windows Vista SP1. O diagrama a seguir mostra o suporte à interoperabilidade entre APIs.
Neste diagrama, as setas mostram cenários de interoperabilidade nos quais a mesma superfície pode ser acessada pelas APIs conectadas. As setas azuis indicam mecanismos de interoperabilidade introduzidos no Windows Vista. As setas verdes indicam suporte de interoperabilidade para novas APIs ou melhorias que ajudam APIs anteriores a interoperar com APIs mais recentes. Por exemplo, as setas verdes representam o compartilhamento de dispositivos, o suporte de superfície compartilhada sincronizada, o auxiliar de sincronização Direct3D 9Ex/DXGI e a obtenção de um contexto de dispositivo GDI de uma superfície compatível.
Cenários de interoperabilidade
A partir do Windows 7 e do Windows Vista 7IP, as principais ofertas de APIs gráficas do Windows dão suporte a várias APIs renderizadas para a mesma superfície DXGI 1.1.
Direct3D 11, Direct3D 10.1, Direct2D – Interoperabilidade entre si
As APIs Direct3D 11, Direct3D 10.1 e Direct2D (e as APIs relacionadas, como DirectWrite e WIC) podem interoperar entre si usando o compartilhamento de dispositivos Direct3D 10.1 ou superfícies compartilhadas sincronizadas.
Compartilhamento de dispositivos Direct3D 10.1 com Direct2D
O compartilhamento de dispositivos entre Direct2D e Direct3D 10.1 permite que um aplicativo use ambas as APIs para renderizar de maneira contínua e eficiente na mesma superfície DXGI 1.1, usando o mesmo objeto de dispositivo Direct3D subjacente. O Direct2D oferece a possibilidade de chamar APIs do Direct2D usando um dispositivo Direct3D 10.1 existente, aproveitando o fato de que o Direct2D é criado com base nos runtimes do Direct3D 10.1 e da DXGI 1.1. Os trechos de código a seguir ilustram como o Direct2D obtém o destino de renderização do dispositivo Direct3D 10.1 de uma superfície DXGI 1.1 associada ao dispositivo. O destino de renderização do dispositivo Direct3D 10.1 pode executar chamadas de desenho Direct2D entre as APIs BeginDraw e 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);
Comentários
- O dispositivo Direct3D 10.1 associado deve dar suporte ao formato BGRA. Este dispositivo foi criado chamando D3D10CreateDevice1 com o parâmetro D3D10_CREATE_DEVICE_BGRA_SUPPORT. Há suporte para o formato BGRA a partir do nível de recurso 9.1 do Direct3D 10.
- O aplicativo não deve criar vários ID2D1RenderTargets associados ao mesmo dispositivo Direct3D10.1.
- Para obter o desempenho ideal, mantenha pelo menos um recurso sempre por perto, como texturas ou superfícies associadas ao dispositivo.
O compartilhamento de dispositivos é indicado para uso em processo e thread único de um dispositivo de renderização compartilhado pelas APIs de renderização Direct3D 10.1 e Direct2D. As superfícies compartilhadas sincronizadas permitem o uso multithread, em processo e fora do processo de vários dispositivos de renderização usados pelas APIs Direct3D 10.1, Direct2D e Direct3D 11.
Outro método de interoperabilidade do Direct3D 10.1 e do Direct2D é usando ID3D1RenderTarget::CreateSharedBitmap, que cria um objeto ID2D1Bitmap de IDXGISurface. Você pode gravar uma cena Direct3D10.1 no bitmap e renderizá-la com Direct2D. Para obter mais informações, consulte Método ID2D1RenderTarget::CreateSharedBitmap.
Rasterização de software Direct2D
Não há suporte para o compartilhamento de dispositivos com Direct3D 10.1 durante o uso do renderizador de software Direct2D, por exemplo, especificando D2D1_RENDER_TARGET_USAGE_FORCE_SOFTWARE_RENDERING em D2D1_RENDER_TARGET_USAGE ao criar um destino de renderização Direct2D.
O Direct2D pode usar o rasterizador de software WARP10 para compartilhar o dispositivo com o Direct3D 10 ou o Direct3D 11, mas o desempenho diminui significativamente.
Superfícies compartilhadas sincronizadas DXGI 1.1
As APIs Direct3D 11, Direct3D 10.1 e Direct2D usam a DXGI 1.1, que oferece a funcionalidade para sincronizar a leitura e a gravação na mesma superfície de memória de vídeo (DXGISurface1) por dois ou mais dispositivos Direct3D. Os dispositivos de renderização que usam superfícies compartilhadas sincronizadas podem ser dispositivos Direct3D 10.1 ou Direct3D 11, cada um em execução no mesmo processo ou entre processos.
Os aplicativos podem usar superfícies compartilhadas sincronizadas para interoperar entre qualquer dispositivo com base na DXGI 1.1, como Direct3D 11 e Direct3D 10.1, ou entre Direct3D 11 e Direct2D, obtendo o dispositivo Direct3D 10.1 do objeto de destino de renderização Direct2D.
No Direct3D 10.1 e APIs posteriores, para usar a DXGI 1.1, verifique se o dispositivo Direct3D foi criado usando um objeto de adaptador DXGI 1.1, enumerado pelo objeto de fábrica DXGI 1.1. Chame CreateDXGIFactory1 para criar o objeto IDXGIFactory1 e EnumAdapters1 para enumerar o objeto IDXGIAdapter1. O objeto IDXGIAdapter1 precisa ser passado como parte da chamada D3D10CreateDevice ou D3D10CreateDeviceAndSwapChain. Para obter mais informações sobre as APIs da DXGI 1.1, consulte o Guia de programação da DXGI.
APIs
D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX
Ao criar o recurso compartilhado sincronizado, defina D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX em 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 que o recurso criado seja sincronizado usando as APIs IDXGIKeyedMutex::AcquireSync e ReleaseSync. As APIs Direct3D 10.1 de criação de recursos a seguir, que usam um parâmetro D3D10_RESOURCE_MISC_FLAG, foram estendidas para dar suporte ao novo sinalizador.
- ID3D10Device1::CreateTexture1D
- ID3D10Device1::CreateTexture2D
- ID3D10Device1::CreateTexture3D
- ID3D10Device1::CreateBuffer
Se qualquer uma das funções listadas for chamada com o sinalizador D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX definido, a interface retornada poderá ser consultada para uma interface IDXGIKeyedMutex, que implementa APIs AcquireSync e ReleaseSync para sincronizar o acesso à superfície. O dispositivo que cria a superfície e qualquer outro dispositivo que abre a superfície (usando OpenSharedResource) é necessário para chamar IDXGIKeyedMutex::AcquireSync antes de qualquer comando de renderização para a superfície e IDXGIKeyedMutex::ReleaseSync quando terminar de renderizar.
Os dispositivos WARP e REF não dão suporte a recursos compartilhados. A tentativa de criar um recurso com esse sinalizador em um dispositivo WARP ou REF fará com que o método create retorne um código de erro E_OUTOFMEMORY.
IDXGIKEYEDMUTEX INTERFACE
Uma nova interface na DXGI 1.1, IDXGIKeyedMutex, representa uma exclusão mútua codificada, que permite acesso exclusivo a um recurso compartilhado usado por vários dispositivos. Para obter a documentação de referência sobre essa interface e seus dois métodos, AcquireSync e ReleaseSync, consulte IDXGIKeyedMutex.
Exemplo: compartilhamento de superfície sincronizada entre dois dispositivos Direct3D 10.1
O exemplo a seguir ilustra o compartilhamento de uma superfície entre dois dispositivos Direct3D 10.1. A superfície compartilhada sincronizada é criada por um dispositivo Direct3D10.1.
// 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;
O mesmo dispositivo Direct3D10.1 pode obter a superfície compartilhada sincronizada para renderização chamando AcquireSync e, em seguida, liberando a superfície para a renderização do outro dispositivo chamando ReleaseSync. Quando não estiver compartilhando a superfície compartilhada sincronizada com qualquer outro dispositivo Direct3D, o criador poderá obter e liberar a superfície compartilhada sincronizada (para iniciar e terminar a renderização) adquirindo e liberar usando o mesmo valor de chave.
// 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;
O segundo dispositivo Direct3D10.1 pode obter a superfície compartilhada sincronizada para renderização chamando AcquireSync e, em seguida, liberando a superfície para a renderização do primeiro dispositivo chamando ReleaseSync. O dispositivo 2 é capaz de adquirir a superfície compartilhada sincronizada usando o mesmo valor de chave especificado na chamada ReleaseSync pelo 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;
Dispositivos adicionais que compartilham a mesma superfície podem se revezar para adquirir e liberar a superfície usando chaves adicionais, conforme mostrado nas chamadas a seguir.
// 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);
...
Um aplicativo do mundo real sempre pode renderizar em uma superfície intermediária que acaba sendo copiada para a superfície compartilhada para evitar que qualquer dispositivo aguarde outro dispositivo com o qual compartilha a superfície.
Usando superfícies compartilhadas sincronizadas com Direct2D e Direct3D 11
Da mesma forma, para compartilhamento entre as APIs Direct3D 11 e Direct3D 10.1, uma superfície compartilhada sincronizada pode ser criada a partir de qualquer dispositivo de API e compartilhada com os outros dispositivos de API, dentro ou fora do processo.
Os aplicativos que usam Direct2D podem compartilhar um dispositivo Direct3D 10.1 e usar uma superfície compartilhada sincronizada para interoperar com o Direct3D 11 ou outros dispositivos Direct3D 10.1, independentemente de pertencerem ao mesmo processo ou a processos diferentes. No entanto, para aplicativos de processo e thread únicos, o compartilhamento de dispositivos é o método de interoperabilidade mais eficiente e de alto desempenho entre o Direct2D e o Direct3D 10 ou o Direct3D 11.
Rasterizador de software
Não há suporte para superfícies compartilhadas sincronizadas quando os aplicativos usam rasterizadores de software Direct3D ou Direct2D, inclusive o rasterizador de referência e o WARP, em vez de usar a aceleração de hardware gráfico.
Interoperabilidade entre APIs baseadas em Direct3D 9Ex e DXGI
As APIs do Direct3D 9Ex incluíam a noção de compartilhamento de superfície para permitir que outras APIs lessem na superfície compartilhada. Para compartilhar a leitura e a gravação em uma superfície compartilhada do Direct3D 9Ex, você deve adicionar a sincronização manual ao próprio aplicativo.
Superfícies compartilhadas do Direct3D 9Ex mais auxiliar de sincronização manual
A tarefa mais fundamental na interoperabilidade do Direct3D 9Ex e do Direct3D 10 ou 11 é passar uma única superfície do primeiro dispositivo (dispositivo A) para o segundo (dispositivo B), de maneira que, quando o dispositivo B adquirir um identificador na superfície, a renderização do dispositivo A tenha a garantia de ter sido concluída. Por isso, o dispositivo B pode usar essa superfície sem se preocupar. Isso é muito semelhante ao clássico problema produtor-consumidor, e essa discussão modela o problema assim. O primeiro dispositivo que usa a superfície e depois a abandona é o produtor (dispositivo A), e o dispositivo que está inicialmente aguardando é o consumidor (dispositivo B). Qualquer aplicativo do mundo real é mais sofisticado do que isso e encadeará vários blocos de construção produtor-consumidor para criar a funcionalidade desejada.
Os blocos de construção produtor-consumidor são implementados no auxiliar usando uma fila de superfícies. As superfícies são enfileiradas pelo produtor e removidas da fila pelo consumidor. O auxiliar apresenta três interfaces COM: ISurfaceQueue, ISurfaceProducer e ISurfaceConsumer.
Visão geral de alto nível do auxiliar
O objeto ISurfaceQueue é o bloco de construção para usar as superfícies compartilhadas. Ele é criado com um dispositivo Direct3D inicializado e uma descrição para criar um número fixo de superfícies compartilhadas. O objeto queue gerencia a criação de recursos e a abertura de código. O número e o tipo de superfícies são fixos; depois que as superfícies forem criadas, o aplicativo não poderá adicionar nem removê-las.
Cada instância do objeto ISurfaceQueue oferece uma espécie de via de mão única que pode ser usada para enviar superfícies do dispositivo de produção para o dispositivo de consumo. Várias dessas ruas de mão única podem ser usadas para permitir cenários de compartilhamento de superfície entre dispositivos de aplicativos específicos.
Criação/Tempo de vida do objeto
Existem duas maneiras de criar o objeto queue: por meio de CreateSurfaceQueue ou por meio do método Clone de ISurfaceQueue. Como as interfaces são objetos COM, o gerenciamento do tempo de vida COM padrão se aplica.
Modelo Produtor/Consumidor
Enqueue (): o produtor chama essa função para indicar que ela foi concluída com a superfície, que agora pode ser disponibilizada para outro dispositivo. Ao retornar dessa função, o dispositivo produtor não tem mais direitos sobre a superfície e não é seguro continuar o usando.
Dequeue (): o dispositivo consumidor chama essa função para obter uma superfície compartilhada. A API garante que todas as superfícies removidas da fila estejam prontas para serem usadas.
Metadados
A API dá suporte à associação de metadados a superfícies compartilhadas.
Enqueue() tem a opção de especificar metadados adicionais que serão passados para o dispositivo consumidor. Os metadados devem ser menores que o máximo conhecido no momento da criação.
Dequeue() também pode passar um buffer e um ponteiro para o tamanho do buffer. A fila preenche o buffer com os metadados da chamada Enqueue correspondente.
Clonar
Cada objeto ISurfaceQueue resolve uma sincronização unidirecional. Vamos pressupor que a grande maioria dos aplicativos que utilizam essa API use um sistema fechado. O sistema fechado mais simples com dois dispositivos enviando superfícies para frente e para trás requer duas filas. O objeto ISurfaceQueue tem um método Clone() para possibilitar a criação de várias filas que fazem parte do mesmo pipeline maior.
Clone cria um novo objeto ISurfaceQueue a partir de um existente e compartilha todos os recursos abertos entre eles. O objeto resultante tem exatamente as mesmas superfícies que a fila de origem. As filas clonadas podem ter tamanhos de metadados diferentes entre si.
de trabalho
O ISurfaceQueue assume a responsabilidade de criar e gerenciar as superfícies. Não é válido enfileirar superfícies arbitrárias. Além disso, uma superfície deve ter apenas um "proprietário" ativo. Ele deve estar em uma fila específica ou sendo usado por um dispositivo específico. Não é válido tê-lo em várias filas ou para que os dispositivos continuem usando a superfície depois que ele for enfileirado.
Detalhes API
IsurfaceQueue
A fila é responsável por criar e manter os recursos compartilhados. Ele também oferece a funcionalidade de encadear várias filas usando Clone. A fila tem métodos que abrem o dispositivo de produção e um dispositivo consumidor. Somente um de cada pode ser aberto a qualquer momento.
A fila expõe as seguintes APIs:
API | Descrição |
---|---|
CreateSurfaceQueue | Cria um objeto ISurfaceQueue (a fila "raiz"). |
ISurfaceQueue::OpenConsumer | Retorna uma interface para o dispositivo de consumo remover da fila. |
ISurfaceQueue::OpenProducer | Retorna uma interface para o dispositivo de produção enfileirar. |
ISurfaceQueue::Clone | Cria um objeto ISurfaceQueue que compartilha superfícies com o objeto de fila raiz. |
CreateSurfaceQueue
typedef struct SURFACE_QUEUE_DESC {
UINT Width;
UINT Height;
DXGI_FORMAT Format;
UINT NumSurfaces;
UINT MetaDataSize;
DWORD Flags;
} SURFACE_QUEUE_DESC;
Membros
Width, Height As dimensões das superfícies compartilhadas. Todas as superfícies compartilhadas devem ter as mesmas dimensões.
Format O formato das superfícies compartilhadas. Todas as superfícies compartilhadas devem ter o mesmo formato. Os formatos válidos dependem dos dispositivos que serão usados, pois pares de dispositivos diferentes podem compartilhar diferentes tipos de formato.
NumSurfaces O número de superfícies que fazem parte da fila. Este é um número fixo.
MetaDataSize O tamanho máximo do buffer de metadados.
Flags Sinalizadores para controlar o comportamento da fila. Consulte Observações.
HRESULT CreateSurfaceQueue(
[in] SURFACE_QUEUE_DESC *pDesc,
[in] IUnknown *pDevice,
[out] IDXGIXSurfaceQueue **ppQueue
);
Parâmetros
pDesc [in] A descrição da fila de superfície compartilhada a ser criada.
pDevice [in] O dispositivo que deve ser usado para criar as superfícies compartilhadas. Este é um parâmetro explícito por causa de um recurso do Windows Vista. Para superfícies compartilhadas entre o Direct3D 9 e o Direct3D 10, as superfícies devem ser criadas com o Direct3D 9.
ppQueue [out] No retorno, contém um ponteiro para o objeto ISurfaceQueue .
Valores retornados
Se pDevice não for capaz de compartilhar recursos, essa função retornará DXGI_ERROR_INVALID_CALL. Essa função cria os recursos. Se falhar, retornará um erro. Se for bem-sucedido, retornará S_OK.
Comentários
A criação do objeto queue também cria todas as superfícies. Todas as superfícies são consideradas destinos de renderização 2D e serão criadas com os sinalizadores D3D10_BIND_RENDER_TARGET e D3D10_BIND_SHADER_RESOURCE definidos (ou os sinalizadores equivalentes para os diferentes tempos de execução).
O desenvolvedor pode especificar um sinalizador que indica se a fila será acessada por vários threads. Se nenhum sinalizador for definido (Flags == 0), a fila será usada por vários threads. O desenvolvedor pode especificar o acesso de thread único, que desativa o código de sincronização e oferece uma melhoria de desempenho para esses casos. Cada fila clonada tem o próprio sinalizador, logo, é possível que diferentes filas no sistema tenham controles de sincronização diferentes.
Abrir um produtor
HRESULT OpenProducer(
[in] IUnknown *pDevice,
[out] IDXGIXSurfaceProducer **ppProducer
);
Parâmetros
pDevice [in]
O dispositivo produtor que enfileira superfícies na fila de superfície.
ppProducer [out] Retorna um objeto para a interface do produtor.
Valores retornados
Se não for capaz de compartilhar superfícies, o dispositivo retornará DXGI_ERROR_INVALID_CALL.
Abrir um consumidor
HRESULT OpenConsumer(
[in] IUnknown *pDevice,
[out] IDXGIXSurfaceConsumer **ppConsumer
);
Parâmetros
pDevice [in]
O dispositivo consumidor que remove superfícies da fila de superfície.
ppConsumer [out] Retorna um objeto para a interface do consumidor.
Valores retornados
Se não for capaz de compartilhar superfícies, o dispositivo retornará DXGI_ERROR_INVALID_CALL.
Comentários
Essa função abre todas as superfícies na fila do dispositivo de entrada e as armazena em cache. As chamadas subsequentes para Dequeue simplesmente vão para o cache e não precisarão reabrir as superfícies sempre.
Clonagem de um IDXGIXSurfaceQueue
typedef struct SHARED_SURFACE_QUEUE_CLONE_DESC {
UINT MetaDataSize;
DWORD Flags;
} SHARED_SURFACE_QUEUE_CLONE_DESC;
Os membros MetaDataSize e Flags têm o mesmo comportamento que têm para CreateSurfaceQueue.
HRESULT Clone(
[in] SHARED_SURFACE_QUEUE_CLONE_DESC *pDesc,
[out] IDXGIXSurfaceQueue **ppQueue
);
Parâmetros
pDesc [in] Um struct que oferece uma descrição do objeto Clone a ser criado. O parâmetro deve ser inicializado.
ppQueue [out] Retorna o objeto inicializado.
Comentários
Você pode clonar de qualquer objeto de fila existente, mesmo que não seja a raiz.
IDXGIXSurfaceConsumer
- Para um IDirect3DDevice9, o REFIID deve ser __uuidof(IDirect3DTexture9).
- Para um ID3D10Device, o REFIID deve ser __uuidof(ID3D10Texture2D).
- Para um ID3D11Device, o REFIID deve ser __uuidof(ID3D11Texture2D).
HRESULT Dequeue(
[in] REFIID id,
[out] void **ppSurface,
[in,out] void *pBuffer,
[in,out] UINT *pBufferSize,
[in] DWORD dwTimeout
);
Parâmetros
id [in]
O REFIID de uma superfície 2D do dispositivo consumidor.
ppSurface [out] Retorna um ponteiro para a superfície.
pBuffer [in, out] Um parâmetro opcional e, se não for NULL, no retorno, conterá os metadados que foram passados na chamada de enfileiramento correspondente.
pBufferSize [in, out] O tamanho de pBuffer, em bytes. Retorna o número de bytes retornados em pBuffer. Se a chamada de enfileiramento não fornecer metadados, pBuffer será definido como 0.
dwTimeout [in] Especifica um valor de tempo limite. Consulte Comentários para obter mais detalhes.
Valores retornados
Essa função poderá retornar WAIT_TIMEOUT se um valor de tempo limite for especificado e a função não retornar antes do valor de tempo limite. Consulte Observações. Se nenhuma superfície estiver disponível, a função retornará com ppSurface definido como NULL, pBufferSize definido como 0, e o valor retornado será 0x80070120 (WIN32_TO_HRESULT(WAIT_TIMEOUT)).
Comentários
Essa API poderá ser bloqueada se a fila estiver vazia. O parâmetro dwTimeout funciona de maneira idêntica às APIs de sincronização do Windows, como WaitForSingleObject. Para comportamento sem bloqueio, use um tempo limite de 0.
ISurfaceProducer
Essa interface oferece dois métodos que permitem que o aplicativo enfileire superfícies. Depois que uma superfície for enfileirada, o ponteiro de superfície deixará de ser válido e não será seguro usá-lo. A única ação que o aplicativo deve realizar com o ponteiro é liberá-lo.
Método | Descrição |
---|---|
ISurfaceProducer::Enqueue | Enfileira uma superfície para o objeto de fila. Depois da conclusão dessa chamada, o produtor encerrará a superfície, e a superfície está pronta para outro dispositivo. |
ISurfaceProducer::Flush | Usado se os aplicativos precisarem ter um comportamento sem bloqueio. Consulte Comentários para obter detalhes. |
Enfileirar
HRESULT Enqueue(
[in] IUnknown *pSurface,
[in] void *pBuffer,
[in] UINT BufferSize,
[in] DWORD Flags
);
Parâmetros
pSurface [in]
A superfície do dispositivo de produção que precisa ser enfileirada. Essa superfície deve ser uma superfície removida da fila da mesma rede de filas. pBuffer [in] Um parâmetro opcional, que é usado para passar metadados. Ele deve apontar para os dados que serão passados para a chamada de remoção da fila.
BufferSize [in] O tamanho de pBuffer, em bytes.
Flags [in] Um parâmetro opcional que controla o comportamento dessa função. O único sinalizador é SURFACE_QUEUE_FLAG_ DO_NOT_WAIT. Consulte as observações de Flush. Se nenhum sinalizador for passado (Flags == 0), o comportamento de bloqueio padrão será usado.
Valores retornados
Essa função poderá retornar DXGI_ERROR_WAS_STILL_DRAWING se um sinalizador SURFACE_QUEUE_FLAG_DO_NOT_WAIT for usado.
Comentários
- Essa função coloca a superfície na fila. Se o aplicativo não especificar SURFACE_QUEUE_FLAG_DO_NOT_WAIT, essa função será bloqueada e fará uma sincronização GPU-CPU para garantir que toda a renderização na superfície enfileirada seja concluída. Se essa função for bem-sucedida, uma superfície estará disponível para remoção da fila. Se você quiser um comportamento sem bloqueio, use o sinalizador DO_NOT_WAIT. Consulte Flush() para obter detalhes.
- De acordo com as regras de contagem de referência COM, a superfície retornada por Dequeue será AddRef(), de maneira que o aplicativo não precise fazer isso. Depois de chamar Enqueue, o aplicativo deverá liberar a superfície porque não a está mais usando.
Liberar
HRESULT Flush(
[in] DWORD Flags,
[out] UINT *nSurfaces
);
Parâmetros
Flags [in]
O único sinalizador é SURFACE_QUEUE_FLAG_ DO_NOT_WAIT. Consulte Observações. nSurfaces [out] Retorna o número de superfícies que ainda estão pendentes e não liberadas.
Valores retornados
Essa função poderá retornar DXGI_ERROR_WAS_STILL_DRAWING se o sinalizador SURFACE_QUEUE_FLAG_DO_NOT_WAIT for usado. Esta função retornará S_OK se alguma superfície tiver sido liberada com êxito. Esta função só retornará DXGI_ERROR_WAS_STILL_DRAWING se nenhuma superfície foi liberada. Juntos, o valor retornado e nSurfaces indicam ao aplicativo qual trabalho foi feito e se ainda há algum trabalho a ser feito.
Comentários
Flush só será significativo se a chamada anterior para enfileirar tiver usado o sinalizador DO_NOT_WAIT; do contrário, será um no-op. Se a chamada para enfileirar tiver usado o sinalizador DO_NOT_WAIT, enfileirar retornará imediatamente, e a sincronização GPU-CPU não será garantida. A superfície ainda é considerada enfileirada, o dispositivo de produção não pode continuar usando-a, mas não está disponível para remoção da fila. Para tentar confirmar a superfície para remoção da fila, Flush deve ser chamado. Flush tenta confirmar todas as superfícies que estão atualmente enfileiradas. Se nenhum sinalizador for passado para Flush, ele bloqueará e limpará toda a fila, preparando todas as superfícies para remoção da fila. Se o sinalizador DO_NOT_WAIT for usado, a fila verificará as superfícies para ver se alguma delas está pronta; esta etapa não é obstrutiva. As superfícies que concluíram a sincronização GPU-CPU estarão prontas para o dispositivo do consumidor. As superfícies que ainda estão pendentes não serão afetadas. A função retorna o número de superfícies que ainda precisam ser liberadas.
Observação
Flush não interromperá a semântica da fila. A API garante que as superfícies enfileiradas primeiro serão confirmadas antes das superfícies enfileiradas posteriormente, independentemente de quando a sincronização GPU-CPU ocorrer.
Direct3D 9Ex e auxiliar de interoperabilidade DXGI: como usar
A expectativa é de que a maioria dos casos de uso envolva dois dispositivos compartilhando várias superfícies. Como esse também é o cenário mais simples, este artigo detalha como usar as APIs para atingir esse objetivo, aborda uma variação não obstrutiva e termina com uma breve seção sobre inicialização para três dispositivos.
Dois dispositivos
O aplicativo de exemplo que usa esse auxiliar pode usar o Direct3D 9Ex e o Direct3D 11 juntos. O aplicativo pode processar conteúdo com ambos os dispositivos e apresentar conteúdo usando o Direct3D 9. Processamento pode significar renderizar conteúdo, decodificar vídeo, executar sombreadores de computação e assim por diante. Para cada quadro, o aplicativo será processado primeiro com o Direct3D 11, depois processará com o Direct3D 9 e, por fim, apresentará com o Direct3D 9. Além disso, o processamento com o Direct3D 11 produzirá alguns metadados que o Direct3D 9 presente precisará consumir. Esta seção aborda o uso do auxiliar em três partes que correspondem a essa sequência: Inicialização, Loop principal e Limpeza.
Inicialização
A inicialização envolve as seguintes etapas:
- Inicialize ambos os dispositivos.
- Crie a fila raiz: m_11to9Queue.
- Clone da fila raiz: m_9to11Queue.
- Chame OpenProducer/OpenConsumer em ambas as filas.
Os nomes das filas usam os números 9 e 11 para indicar qual API é o produtor e qual é o consumidor: m_producertoconsumerQueue. Da mesma forma, m_11to9Queue indica uma fila para a qual o dispositivo Direct3D 11 produz superfícies que o dispositivo Direct3D 9 consome. Da mesma forma, m_9to11Queue indica uma fila para a qual o Direct3D 9 produz superfícies que o Direct3D 11 consome.
A fila raiz está inicialmente cheia e todas as filas clonadas estão inicialmente vazias. Isso não deve ser um problema para o aplicativo, exceto para o primeiro ciclo de enfileiramentos e remoções da fila, além da disponibilidade de metadados. Se um dequeue solicitar metadados, mas nenhum foi definido (seja porque nada está lá inicialmente ou enqueue não definiu nada), dequeue verá que nenhum metadado foi recebido.
Inicialize ambos os dispositivos.
m_pD3D9Device = InitializeD3D9ExDevice(); m_pD3D11Device = InitializeD3D11Device();
Crie a fila raiz.
Esta etapa também cria as superfícies. As restrições de tamanho e formato são idênticas à criação de qualquer recurso compartilhado. O tamanho do buffer de metadados é fixado no momento da criação e, nesse caso, passaremos apenas um UINT.
A fila deve ser criada com um número fixo de superfícies. O desempenho vai variar de acordo com o cenário. Ter várias superfícies aumenta as chances de que os dispositivos estejam ocupados. Por exemplo, se houver apenas uma superfície, não haverá paralelização entre os dois dispositivos. Por outro lado, o aumento do número de superfícies amplia o volume de memória, o que pode prejudicar o desempenho. Este exemplo usa duas superfícies.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);
Clone a fila raiz.
Cada fila clonada deve usar as mesmas superfícies, mas pode ter tamanhos de buffer de metadados e sinalizadores diferentes. Nesse caso, não há metadados do Direct3D 9 para o Direct3D 11.SURFACE_QUEUE_CLONE_DESC Desc; Desc.MetaDataSize = 0; Desc.Flags = 0; m_11to9Queue->Clone(&Desc, &m_9to11Queue);
Abra os dispositivos de produtor e consumidor.
O aplicativo deve realizar essa etapa antes de chamar Enqueue e Dequeue. A abertura de um produtor e consumidor retorna interfaces que contêm as APIs de enfileiramento/remoção da fila.// 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);
Loop principal
O uso da fila será modelado depois do problema clássico do produtor/consumidor. Pense nisso de um ponto de vista por dispositivo. Cada dispositivo deve realizar essas etapas: remover da fila para obter uma superfície da fila de consumo, processar na superfície e, em seguida, enfileirar na fila de produção. Para o dispositivo Direct3D 11, o uso do Direct3D 9 é quase 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);
}
Limpeza
Esta etapa é muito simples. Além das etapas normais para limpar as APIs do Direct3D, o aplicativo deve liberar as interfaces COM de retorno.
m_pD3D9Producer->Release();
m_pD3D9Consumer->Release();
m_pD3D11Producer->Release();
m_pD3D11Consumer->Release();
m_p9to11Queue->Release();
m_p11to9Queue->Release();
Uso não obstrutivo
O exemplo anterior faz sentido para um caso de uso multithread em que cada dispositivo tem o próprio thread. O exemplo usa as versões de bloqueio das APIs: INFINITE para tempo limite e nenhum sinalizador para enfileiramento. Se quiser usar o auxiliar de maneira não obstrutiva, você só precisará fazer algumas alterações. Esta seção mostra o uso não obstrutivo com ambos os dispositivos em um thread.
Inicialização
A inicialização é idêntica, exceto pelos sinalizadores. Como o aplicativo é de thread único, use esse sinalizador na criação. Isso desativa parte do código de sincronização, o que potencialmente melhora o desempenho.
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);
A abertura dos dispositivos do produtor e do consumidor é a mesma do exemplo de bloqueio.
Uso de Queue
Existem muitas maneiras de usar a fila de maneira não obstrutiva com várias características de desempenho. O exemplo a seguir é simples, mas tem baixo desempenho devido ao excesso de rotação e sondagem. Apesar desses problemas, o exemplo mostra como usar o auxiliar. A abordagem é permanecer constantemente em um loop e remover da fila, processar, enfileirar e liberar. Se alguma das etapas falhar porque o recurso não está disponível, o aplicativo simplesmente tentará novamente o próximo loop.
// 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);
}
Uma solução mais complexa pode verificar o valor retornado de enqueue e de flush para determinar se a liberação é necessária.
Três dispositivos
A extensão dos exemplos anteriores para abranger vários dispositivos é simples. O código a seguir realiza a inicialização. Depois que os objetos Producer/Consumer forem criados, o código para usá-los será o mesmo. Este exemplo tem três dispositivos e, por isso, três filas. As superfícies fluem do Direct3D 9 para o Direct3D 10 e para o 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);
Conforme mencionado anteriormente, a clonagem funciona da mesma maneira, independentemente da fila clonada. Por exemplo, a segunda chamada Clone pode ter sido desativada do 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);
Conclusão
Você pode criar soluções que usam interoperabilidade para empregar o poder de várias APIs do DirectX. A interoperabilidade da API de gráficos do Windows agora oferece um tempo de execução de gerenciamento de superfície comum DXGI 1.1. Esse tempo de execução permite o suporte ao compartilhamento de superfície sincronizado em APIs recém-desenvolvidas, como Direct3D 11, Direct3D 10.1 e Direct2D. As melhorias de interoperabilidade entre novas APIs e APIs existentes ajudam na migração de aplicativos e na compatibilidade com versões anteriores. As APIs de consumidor do Direct3D 9Ex e do DXGI 1.1 podem interoperar, conforme mostrado com o mecanismo de sincronização disponível em aplicativos de exemplo Win32 mais antigos que podem ser encontrados no repositório Exemplo de Galeria de Códigos do MSDN da Microsoft arquivado.