Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Você pode incluir conteúdo Direct3D9 em um aplicativo Windows Presentation Foundation (WPF). Este tópico descreve como criar conteúdo Direct3D9 para que ele interopere eficientemente com o WPF.
Observação
Ao usar conteúdo Direct3D9 no WPF, você também precisa pensar no desempenho. Para obter mais informações sobre como otimizar o desempenho, consulte Considerações de desempenho para interoperabilidade do Direct3D9 e WPF.
Buffers de exibição
A D3DImage classe gerencia dois buffers de exibição, que são chamados de buffer traseiro e buffer frontal. O buffer traseiro é a superfície do Direct3D9. As alterações no buffer traseiro são copiadas para o buffer frontal quando você chama o Unlock método.
A ilustração a seguir mostra a relação entre o buffer traseiro e o buffer frontal.
Criação de dispositivos Direct3D9
Para renderizar conteúdo Direct3D9, você deve criar um dispositivo Direct3D9. Há dois objetos Direct3D9 que você pode usar para criar um dispositivo IDirect3D9 e IDirect3D9Ex. Use esses objetos para criar IDirect3DDevice9 e IDirect3DDevice9Ex dispositivos, respectivamente.
Crie um dispositivo chamando um dos seguintes métodos.
IDirect3D9 * Direct3DCreate9(UINT SDKVersion);HRESULT Direct3DCreate9Ex(UINT SDKVersion, IDirect3D9Ex **ppD3D);
No sistema operacional Windows Vista ou posterior, use o Direct3DCreate9Ex método com um monitor configurado para usar o WDDM (Windows Display Driver Model). Use o Direct3DCreate9 método em qualquer outra plataforma.
Disponibilidade do método Direct3DCreate9Ex
O d3d9.dll tem o Direct3DCreate9Ex método somente no Windows Vista ou num sistema operacional posterior. Se você vincular diretamente a função no Windows XP, seu aplicativo não será carregado. Para determinar se o Direct3DCreate9Ex método é suportado, carregue a DLL e procure o endereço proc. O código a seguir mostra como testar o Direct3DCreate9Ex método. Para obter um exemplo de código completo, consulte Passo a passo: Criando conteúdo Direct3D9 para hospedagem no WPF.
HRESULT
CRendererManager::EnsureD3DObjects()
{
HRESULT hr = S_OK;
HMODULE hD3D = NULL;
if (!m_pD3D)
{
hD3D = LoadLibrary(TEXT("d3d9.dll"));
DIRECT3DCREATE9EXFUNCTION pfnCreate9Ex = (DIRECT3DCREATE9EXFUNCTION)GetProcAddress(hD3D, "Direct3DCreate9Ex");
if (pfnCreate9Ex)
{
IFC((*pfnCreate9Ex)(D3D_SDK_VERSION, &m_pD3DEx));
IFC(m_pD3DEx->QueryInterface(__uuidof(IDirect3D9), reinterpret_cast<void **>(&m_pD3D)));
}
else
{
m_pD3D = Direct3DCreate9(D3D_SDK_VERSION);
if (!m_pD3D)
{
IFC(E_FAIL);
}
}
m_cAdapters = m_pD3D->GetAdapterCount();
}
Cleanup:
if (hD3D)
{
FreeLibrary(hD3D);
}
return hr;
}
Criação de HWND
Criar um dispositivo requer um HWND. Em geral, você cria um HWND fictício para o Direct3D9 usar. O exemplo de código a seguir mostra como criar um HWND fictício.
HRESULT
CRendererManager::EnsureHWND()
{
HRESULT hr = S_OK;
if (!m_hwnd)
{
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = DefWindowProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = NULL;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
IFC(E_FAIL);
}
m_hwnd = CreateWindow(szAppName,
TEXT("D3DImageSample"),
WS_OVERLAPPEDWINDOW,
0, // Initial X
0, // Initial Y
0, // Width
0, // Height
NULL,
NULL,
NULL,
NULL);
}
Cleanup:
return hr;
}
Parâmetros atuais
Criar um dispositivo também requer uma D3DPRESENT_PARAMETERS estrutura, mas apenas alguns parâmetros são importantes. Esses parâmetros são escolhidos para minimizar o espaço ocupado pela memória.
Defina os BackBufferHeight campos e BackBufferWidth como 1. Defini-los como 0 faz com que eles sejam definidos para as dimensões do HWND.
Sempre defina os sinalizadores D3DCREATE_MULTITHREADED e D3DCREATE_FPU_PRESERVE para evitar a corrupção da memória usada pelo Direct3D9 e para impedir que o Direct3D9 altere as configurações da FPU.
O código a seguir mostra como inicializar o D3DPRESENT_PARAMETERS struct.
HRESULT
CRenderer::Init(IDirect3D9 *pD3D, IDirect3D9Ex *pD3DEx, HWND hwnd, UINT uAdapter)
{
HRESULT hr = S_OK;
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
d3dpp.BackBufferHeight = 1;
d3dpp.BackBufferWidth = 1;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
D3DCAPS9 caps;
DWORD dwVertexProcessing;
IFC(pD3D->GetDeviceCaps(uAdapter, D3DDEVTYPE_HAL, &caps));
if ((caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) == D3DDEVCAPS_HWTRANSFORMANDLIGHT)
{
dwVertexProcessing = D3DCREATE_HARDWARE_VERTEXPROCESSING;
}
else
{
dwVertexProcessing = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
}
if (pD3DEx)
{
IDirect3DDevice9Ex *pd3dDevice = NULL;
IFC(pD3DEx->CreateDeviceEx(
uAdapter,
D3DDEVTYPE_HAL,
hwnd,
dwVertexProcessing | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE,
&d3dpp,
NULL,
&m_pd3dDeviceEx
));
IFC(m_pd3dDeviceEx->QueryInterface(__uuidof(IDirect3DDevice9), reinterpret_cast<void**>(&m_pd3dDevice)));
}
else
{
assert(pD3D);
IFC(pD3D->CreateDevice(
uAdapter,
D3DDEVTYPE_HAL,
hwnd,
dwVertexProcessing | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE,
&d3dpp,
&m_pd3dDevice
));
}
Cleanup:
return hr;
}
Criando o destino de renderização do buffer traseiro
Para exibir conteúdo Direct3D9 numa D3DImage, crie uma superfície Direct3D9 e atribua-a chamando o método SetBackBuffer.
Verificando o suporte ao adaptador
Antes de criar uma superfície, verifique se todos os adaptadores suportam as propriedades de superfície necessárias. Mesmo se você renderizar para apenas um adaptador, a janela WPF pode ser exibida em qualquer adaptador no sistema. Você deve sempre escrever código Direct3D9 que lida com configurações multi-adaptador e deve verificar o suporte de todos os adaptadores, pois o WPF pode mover a superfície entre os adaptadores disponíveis.
O exemplo de código a seguir mostra como verificar todos os adaptadores no sistema para suporte a Direct3D9.
HRESULT
CRendererManager::TestSurfaceSettings()
{
HRESULT hr = S_OK;
D3DFORMAT fmt = m_fUseAlpha ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8;
//
// We test all adapters because because we potentially use all adapters.
// But even if this sample only rendered to the default adapter, you
// should check all adapters because WPF may move your surface to
// another adapter for you!
//
for (UINT i = 0; i < m_cAdapters; ++i)
{
// Can we get HW rendering?
IFC(m_pD3D->CheckDeviceType(
i,
D3DDEVTYPE_HAL,
D3DFMT_X8R8G8B8,
fmt,
TRUE
));
// Is the format okay?
IFC(m_pD3D->CheckDeviceFormat(
i,
D3DDEVTYPE_HAL,
D3DFMT_X8R8G8B8,
D3DUSAGE_RENDERTARGET | D3DUSAGE_DYNAMIC, // We'll use dynamic when on XP
D3DRTYPE_SURFACE,
fmt
));
// D3DImage only allows multisampling on 9Ex devices. If we can't
// multisample, overwrite the desired number of samples with 0.
if (m_pD3DEx && m_uNumSamples > 1)
{
assert(m_uNumSamples <= 16);
if (FAILED(m_pD3D->CheckDeviceMultiSampleType(
i,
D3DDEVTYPE_HAL,
fmt,
TRUE,
static_cast<D3DMULTISAMPLE_TYPE>(m_uNumSamples),
NULL
)))
{
m_uNumSamples = 0;
}
}
else
{
m_uNumSamples = 0;
}
}
Cleanup:
return hr;
}
Criar o Surface
Antes de criar uma superfície, verifique se os recursos do dispositivo suportam um bom desempenho no sistema operacional de destino. Para obter mais informações, consulte Considerações de Desempenho para Direct3D9 e Interoperabilidade WPF.
Depois de verificar as capacidades do dispositivo, pode criar a superfície. O exemplo de código a seguir mostra como criar o destino de renderização.
HRESULT
CRenderer::CreateSurface(UINT uWidth, UINT uHeight, bool fUseAlpha, UINT m_uNumSamples)
{
HRESULT hr = S_OK;
SAFE_RELEASE(m_pd3dRTS);
IFC(m_pd3dDevice->CreateRenderTarget(
uWidth,
uHeight,
fUseAlpha ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8,
static_cast<D3DMULTISAMPLE_TYPE>(m_uNumSamples),
0,
m_pd3dDeviceEx ? FALSE : TRUE, // Lockable RT required for good XP perf
&m_pd3dRTS,
NULL
));
IFC(m_pd3dDevice->SetRenderTarget(0, m_pd3dRTS));
Cleanup:
return hr;
}
WDDM
No Windows Vista e sistemas operacionais posteriores, que são configurados para usar o WDDM, você pode criar uma textura de destino de renderização e passar a superfície de nível 0 para o SetBackBuffer método. Essa abordagem não é recomendada no Windows XP, porque você não pode criar uma textura de destino de renderização bloqueável e o desempenho será reduzido.
Estado do dispositivo de manipulação
A D3DImage classe gerencia dois buffers de exibição, que são chamados de buffer traseiro e buffer frontal. O buffer traseiro é a superfície do Direct3D. As alterações no buffer traseiro são copiadas para o buffer frontal quando o método Unlock é chamado, sendo exibido no hardware. Ocasionalmente, o buffer frontal fica indisponível. Essa falta de disponibilidade pode ser causada por bloqueio de tela, aplicativos Direct3D exclusivos em tela cheia, troca de usuário ou outras atividades do sistema. Quando isso ocorre, a sua aplicação WPF é notificada ao manipular o evento IsFrontBufferAvailableChanged. A forma como seu aplicativo responde à indisponibilidade do buffer frontal depende se o WPF está habilitado para retornar à renderização de software. O SetBackBuffer método tem uma sobrecarga que usa um parâmetro que especifica se o WPF retorna à renderização de software.
Quando você chama a SetBackBuffer(D3DResourceType, IntPtr) sobrecarga ou chama a SetBackBuffer(D3DResourceType, IntPtr, Boolean) sobrecarga com o enableSoftwareFallback parâmetro definido como false, o sistema de renderização libera sua referência ao buffer traseiro quando o buffer frontal fica indisponível e nada é exibido. Quando o buffer frontal estiver disponível novamente, o sistema de renderização acionará o IsFrontBufferAvailableChanged evento para notificar seu aplicativo WPF. Pode criar um manipulador de eventos para o evento IsFrontBufferAvailableChanged que permitirá reiniciar a renderização com uma superfície Direct3D válida. Para reiniciar a renderização, você deve chamar SetBackBuffer.
Quando você chama a SetBackBuffer(D3DResourceType, IntPtr, Boolean) sobrecarga com o enableSoftwareFallback parâmetro definido como true, o sistema de renderização mantém sua referência ao buffer traseiro quando o buffer frontal fica indisponível, portanto, não há necessidade de chamar SetBackBuffer quando o buffer frontal estiver disponível novamente.
Quando a renderização de software está habilitada, pode haver situações em que o dispositivo do usuário fica indisponível, mas o sistema de renderização mantém uma referência à superfície Direct3D. Para verificar se um dispositivo Direct3D9 não está disponível, chame o TestCooperativeLevel método. Para verificar um dispositivo Direct3D9Ex, chame o CheckDeviceState método, porque o TestCooperativeLevel método foi preterido e sempre retorna êxito. Se o dispositivo do usuário ficou indisponível, chame SetBackBuffer para liberar a referência do WPF para o buffer traseiro. Se você precisar redefinir seu dispositivo, chame SetBackBuffer com o backBuffer parâmetro definido como nulle, em seguida, chame SetBackBuffer novamente com backBuffer definido para uma superfície Direct3D válida.
Chame o método Reset para recuperar de um dispositivo inválido somente se implementar suporte a vários adaptadores. Caso contrário, libere todas as interfaces Direct3D9 e recrie-as completamente. Se o layout do adaptador tiver sido alterado, os objetos Direct3D9 criados antes da alteração não serão atualizados.
Gestão de Redimensionamento
Se a D3DImage for exibida em uma resolução diferente de seu tamanho nativo, ela será dimensionada de acordo com a corrente BitmapScalingMode, exceto que Bilinear é substituída por Fant.
Se precisar de maior fidelidade, deverá criar uma nova superfície quando o contentor do D3DImage for alterado de tamanho.
Existem três abordagens possíveis para lidar com o redimensionamento.
Participe do sistema de layout e crie uma nova superfície quando o tamanho mudar. Não crie demasiadas superfícies, porque pode esgotar ou fragmentar a memória de vídeo.
Aguarde até que um evento de redimensionamento não tenha ocorrido por um período de tempo fixo para criar a nova superfície.
Crie um DispatcherTimer que verifique as dimensões do contêiner várias vezes por segundo.
Otimização de vários monitores
Um desempenho significativamente reduzido pode resultar quando o sistema de renderização move um D3DImage para outro monitor.
No WDDM, desde que os monitores estejam na mesma placa de vídeo e você use Direct3DCreate9Ex, não há redução no desempenho. Se os monitores estiverem em placas de vídeo separadas, o desempenho será reduzido. No Windows XP, o desempenho é sempre reduzido.
Quando o D3DImage move para outro monitor, você pode criar uma nova superfície no adaptador correspondente para restaurar o bom desempenho.
Para evitar a penalidade de desempenho, escreva código especificamente para o caso de vários monitores. A lista a seguir mostra uma maneira de escrever código com vários monitores.
Encontre um ponto do D3DImage espaço da tela usando o
Visual.ProjectToScreenmétodo.Use o
MonitorFromPointmétodo GDI para localizar o monitor que está exibindo o ponto.Use o
IDirect3D9::GetAdapterMonitormétodo para localizar em qual adaptador Direct3D9 o monitor está.Se o adaptador não for o mesmo que o adaptador com o back buffer, crie um novo back buffer no novo monitor e atribua-o ao D3DImage back buffer.
Observação
Se o D3DImage cobrir monitores, o desempenho será lento, exceto no caso de WDDM e IDirect3D9Ex no mesmo adaptador. Não há como melhorar o desempenho nessa situação.
O exemplo de código a seguir mostra como localizar o monitor atual.
void
CRendererManager::SetAdapter(POINT screenSpacePoint)
{
CleanupInvalidDevices();
//
// After CleanupInvalidDevices, we may not have any D3D objects. Rather than
// recreate them here, ignore the adapter update and wait for render to recreate.
//
if (m_pD3D && m_rgRenderers)
{
HMONITOR hMon = MonitorFromPoint(screenSpacePoint, MONITOR_DEFAULTTONULL);
for (UINT i = 0; i < m_cAdapters; ++i)
{
if (hMon == m_pD3D->GetAdapterMonitor(i))
{
m_pCurrentRenderer = m_rgRenderers[i];
break;
}
}
}
}
Atualize o monitor quando o tamanho ou a D3DImage posição do contêiner mudar ou atualize o monitor usando um DispatcherTimer que atualiza algumas vezes por segundo.
Renderização de software WPF
O WPF renderiza de forma síncrona no thread da interface do usuário no software nas seguintes situações.
Impressão
Quando uma dessas situações ocorre, o sistema de renderização chama o CopyBackBuffer método para copiar o buffer de hardware para o software. A implementação padrão chama o método GetRenderTargetData com a sua superfície. Como essa chamada ocorre fora do padrão Bloquear/Desbloquear, ela pode falhar. Nesse caso, o CopyBackBuffer método retorna null e nenhuma imagem é exibida.
Você pode substituir CopyBackBuffer o método, chamar a implementação base e, se ela retornar null, poderá retornar um marcador de posição BitmapSource.
Você também pode implementar sua própria renderização de software em vez de chamar a implementação base.
Observação
Se o WPF estiver renderizando completamente no software, D3DImage não será mostrado porque o WPF não tem um buffer frontal.
Ver também
.NET Desktop feedback