Compartilhar via


Criando conteúdo Direct3D9 que pode ser hospedado no WPF

Você pode incluir conteúdo Direct3D9 em um aplicativo Windows Presentation Foundation (WPF). Este tópico descreve como criar conteúdo de Direct3D9 para que ele interopera com eficiência com o WPF.

ObservaçãoObservação:

Ao usar Direct3D9 conteúdo no WPF, você também necessidade de pensar sobre desempenho . Para obter mais informações sobre como otimizar para desempenho , consulte Considerações sobre desempenho de Direct3D9 e interoperabilidade do WPF.

Buffers de vídeo

The D3DImage classe gerencia dois buffers de vídeo, que são chamados de buffer de fundo and the buffer frontal. O buffer de fundo é a superfície de Direct3D9. Alterações para o buffer de fundo são copiadas adiante para buffer frontal quando você chamar o Unlock método.

A ilustração a seguir mostra a relação entre o buffer de fundo e o buffer frontal.

Buffers de exibição D3DImage

Criação de dispositivos do Direct3D9

Para processar Direct3D9 conteúdo, você deve criar um dispositivo de Direct3D9. Há dois objetos de Direct3D9 que você pode usar para criar um dispositivo, IDirect3D9 e IDirect3D9Ex. Usar 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);

Use o Direct3DCreate9Ex método no Windows Vista com uma exibição que está configurado para usar a WDDM (Windows Display Driver Model). Use o Direct3DCreate9 método em qualquer Outros plataforma.

Disponibilidade do método Direct3DCreate9Ex

Somente o d3d9.dll no Windows Vista tem o Direct3DCreate9Ex método. Se você vincular diretamente a função no Windows XP, seu aplicativo não carrega. Para determinar se o Direct3DCreate9Ex há suporte para o método, carregar a DLL e procure o endereço de proc. O código a seguir mostra como testar o Direct3DCreate9Ex método. Para obter um exemplo de código completo, consulte Demonstra 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

A criação de um dispositivo requer um HWND. Em geral, você pode criar um HWND fictício de 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 de presentes

Criação de um dispositivo também requer um D3DPRESENT_PARAMETERS struct, mas somente alguns parâmetros são importantes. Esses parâmetros são escolhidos para minimizar a superfície de memória.

conjunto o BackBufferHeight e BackBufferWidth campos a 1. faz com conjunto seletiva-las para 0 que eles sejam conjunto às dimensões do HWND.

Defina sempre a D3DCREATE_MULTITHREADED e D3DCREATE_FPU_PRESERVE sinalizadores para evitar danificar a memória usada pelo Direct3D9 e para impedir que Direct3D9 alterem as configurações de 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 Render Buffer voltar

Para exibir o conteúdo de Direct3D9 em um D3DImage, você criar uma superfície de Direct3D9 e atribuí-lo chamando o SetBackBuffer método.

Verificar suporte de adaptador

Antes de criar uma superfície, verifique se todos os adaptadores suporte as propriedades de superfície necessários. Mesmo se você processar a apenas um adaptador, a janela WPF pode ser exibida em qualquer adaptador no sistema. Você deve sempre gravar Direct3D9 código que manipula multi-adaptador configurações, e você deve verificar todos os adaptadores para suporte, porque o WPF pode transferir a superfície entre adaptadores disponível.

O exemplo de código a seguir mostra como verificar todos os adaptadores no sistema de Direct3D9 suporte.

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;
}

A superfície de criação

Antes de criar uma superfície, verifique se os recursos de dispositivo suportam um mercadoria desempenho no sistema operacional de destino. Para obter mais informações, consulte Considerações sobre desempenho de Direct3D9 e interoperabilidade do WPF.

Quando verificar recursos do dispositivo, você pode criar a superfície. O exemplo de código a seguir mostra como criar alvo processado.

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 está configurado 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 não é possível criar uma textura de destino render bloqueáveis e desempenho será reduzido.

Estado do tratamento de dispositivos

Quando o IsFrontBufferAvailable propriedade transições de true para falseWPF não exibe a D3DImage e ele não copiar seu buffer de fundo para o buffer frontal. Em geral, isso significa que o dispositivo foi perdido.

Quando o dispositivo é perdido, seu código deve interromper o renderização e espere o IsFrontBufferAvailable propriedade fazer a transição para true. Manipular o IsFrontBufferAvailableChanged evento para ser notificado sobre essa transição.

Quando o IsFrontBufferAvailable propriedade transições de false para true, você deve verificar o seu dispositivo para determinar se é válido.

Para dispositivos de Direct3D9 telefonar o TestCooperativeLevel método. Para dispositivos Direct3D9Ex telefonar o CheckDeviceState método, porque o TestCooperativeLevel método está obsoleto e sempre retorna sucesso.

Se o dispositivo for válido, telefonar o SetBackBuffer método novamente com a superfície original.

Se o dispositivo inválido for válido, será necessário redefinir o dispositivo e re-criar seus recursos. Chamando o SetBackBuffer método com uma superfície de um dispositivo inválido gera uma exceção.

Chamar o Reset método para recuperar de um dispositivo inválido somente se você implementar suporte multi-adaptador. Caso contrário, solte todas as interfaces de Direct3D9 e re-criar-los completamente. Se tiver alterado o layout do adaptador, objetos de Direct3D9 criados antes da alterar não são atualizados.

Manipulação de redimensionamento

If a D3DImage é exibida em uma resolução diferente de seu dimensionar nativo, ele é dimensionado de acordo com a corrente BitmapScalingMode, exceto que Bilinear é substituída pela Fant.

Se você precisar de uma alta fidelidade, você deve criar um novo de superfície quando o contêiner do D3DImage Altera dimensionar.

Há três abordagens possíveis para lidar com redimensionamento.

  • Participar no sistema de layout e criar uma nova superfície quando o dimensionar é alterado. Não crie muitos superfícies, pois podem usar ou fragmentação de memória de vídeo.

  • Aguarde até que não ocorreu um evento de redimensionar por um período fixo de time para criar a superfície de nova.

  • Criar um DispatcherTimer que verifica as dimensões de contêiner várias vezes por segundo.

Multi-monitor otimização

Significativamente diminuição do desempenho pode ocorrer quando o sistema de renderização move um D3DImage para outro monitor.

No WDDM, desde que sistema autônomo monitores estão no mesmo vídeo placa e você usar Direct3DCreate9Ex, não há nenhuma redução no desempenho. Se os monitores estiverem em placas de vídeo separadas, o desempenho é reduzido. No Windows XP, o desempenho é reduzido sempre.

Quando o D3DImage Move para outro monitor, você pode criar uma superfície de nova no adaptador correspondente restauração um mercadoria desempenho.

Para evitar a perda de desempenho, gravar código especificamente para o caso de vários monitores. A lista a seguir mostra uma maneira de escrever o código de vários monitores.

  1. Localizar um ponto do D3DImage no espaço de tela com o Visual.ProjectToScreen método.

  2. Use o MonitorFromPoint Método GDI para encontrar o monitor está exibindo o ponto.

  3. Use o IDirect3D9::GetAdapterMonitor método para encontrar qual adaptador Direct3D9 o monitor está em.

  4. Se o adaptador não for o mesmo que o adaptador com o buffer de fundo, crie um novo buffer de fundo no novo monitor e atribua a ela para o D3DImage buffer de fundo.

ObservaçãoObservação:

Se a D3DImage straddles monitores, o desempenho será lentos, exceto no caso do WDDM e IDirect3D9Ex no mesmo adaptador. Não é possível melhorar o desempenho nessa situação.

O exemplo de código a seguir mostra como encontrar o monitor corrente.

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;
            }
        }
    }
}

Atualizar o monitor quando o D3DImage alterações de dimensionar ou posição do contêiner ou atualização do monitor usando um DispatcherTimer que atualizações algumas vezes por segundo.

renderização de software do WPF

WPF sincronicamente processa no thread da interface do usuário no software nas seguintes situações.

Quando uma dessas situações ocorrer, o sistema de renderização chama o CopyBackBuffer método para copiar o buffer de hardware para software. A implementação padrão chama o GetRenderTargetData método com sua superfície. Como essa telefonar ocorre fora do padrão de bloquear/desbloquear, ele poderá falhar. Nesse caso, a CopyBackBuffer método retorna null e nenhuma imagem é exibida.

Você pode substituir o CopyBackBuffer método, chame a implementação base, e se ela retorna null, você pode retornar um espaço reservado BitmapSource.

Você também pode implementar seu próprio renderização de software em vez de chamar a implementação base.

ObservaçãoObservação:

I f WPF é processar completamente no software, D3DImage não é mostrado porque WPF não tem um buffer frontal.

Consulte também

Tarefas

Demonstra Passo a passo: Criando conteúdo Direct3D9 para hospedagem no WPF

Demonstra Passo a passo: Hospedagem de conteúdo de Direct3D9 no WPF

Conceitos

Considerações sobre desempenho de Direct3D9 e interoperabilidade do WPF

Referência

D3DImage

Date

History

Motivo

Julho de 2008

Adicione novo tópico.

Alteração de recurso do SP1.