WPF ve Direct3D9 Birlikte Çalışması

Direct3D9 içeriğini bir Windows Presentation Foundation (WPF) uygulamasına ekleyebilirsiniz. Bu konuda, Direct3D9 içeriğinin WPF ile verimli bir şekilde birlikte çalışması için nasıl oluşturulacağı açıklanmaktadır.

Not

WPF 'te Direct3D9 İçeriği kullanırken performans hakkında da düşünmeniz gerekir. Performansı iyileştirmek hakkında daha fazla bilgi için bkz. Direct3D9 ve WPF birlikte çalışabilirliği Için performans konuları.

Ara bellek görüntüle

D3DImageSınıfı, D3DImage ve ön arabellekolarak adlandırılan iki görüntüleme arabelleğini yönetir. Arka arabellek, Direct3D9 yüzeyiniz. Geri arabellek değişiklikleri, yöntemini çağırdığınızda ön arabelleğe doğru kopyalanır Unlock .

Aşağıdaki çizimde, arka arabelleğin ve ön arabelleğin arasındaki ilişki gösterilmektedir.

D3DImage display buffers

Direct3D9 cihaz oluşturma

Direct3D9 içeriğini işlemek için bir Direct3D9 cihazı oluşturmanız gerekir. Bir cihaz oluşturmak için kullanabileceğiniz iki Direct3D9 nesnesi vardır IDirect3D9 ve IDirect3D9Ex . Sırasıyla ve cihazları oluşturmak için bu nesneleri kullanın IDirect3DDevice9IDirect3DDevice9Ex .

Aşağıdaki yöntemlerden birini çağırarak bir cihaz oluşturun.

  • IDirect3D9 * Direct3DCreate9(UINT SDKVersion);

  • HRESULT Direct3DCreate9Ex(UINT SDKVersion, IDirect3D9Ex **ppD3D);

Windows Vista veya sonraki işletim sistemlerinde, Direct3DCreate9Ex Windows görüntü sürücüsü modelini (WDDM) kullanmak üzere yapılandırılmış bir ekran ile yöntemini kullanın. Direct3DCreate9Yöntemini diğer tüm platformlarda kullanın.

Direct3DCreate9Ex yönteminin kullanılabilirliği

d3d9.dll, Direct3DCreate9Ex yalnızca Windows Vista veya sonraki işletim sistemlerinde yöntemi vardır. işlevi Windows XP 'de doğrudan bağlarsanız, uygulamanız yükleme başarısız olur. Direct3DCreate9ExYöntemin desteklenip desteklenmediğini anlamak IÇIN dll 'yi yükleyin ve proc adresini arayın. Aşağıdaki kod yöntemi için nasıl test yapılacağını gösterir Direct3DCreate9Ex . Tam kod örneği için bkz. Izlenecek yol: WPF 'de barındırmak Için Direct3D9 Içeriği oluşturma.

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

HWND oluşturma

Bir cihaz oluşturmak için bir HWND gerekir. Genel olarak, Direct3D9 için kullanmak üzere kukla bir HWND oluşturursunuz. Aşağıdaki kod örneği, bir kukla HWND oluşturmayı gösterir.

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

Mevcut Parametreler

Bir cihaz oluşturmak için de bir D3DPRESENT_PARAMETERS yapı gerekir, ancak yalnızca birkaç parametre önemlidir. Bu parametreler, bellek parmak izini en aza indirmek için seçilir.

BackBufferHeightVe BackBufferWidth alanlarını 1 olarak ayarlayın. Bunların 0 olarak ayarlanması, HWND 'nin boyutlarına ayarlanmasına neden olur.

D3DCREATE_MULTITHREADEDD3DCREATE_FPU_PRESERVE Direct3D9 tarafından kullanılan belleğin bozulmasını engellemek ve Direct3D9 'ın FPU ayarlarını değiştirmesini engellemek için her zaman ve bayraklarını ayarlayın.

Aşağıdaki kod yapının nasıl başlatılacağını gösterir D3DPRESENT_PARAMETERS .

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

Arka arabellek Işleme hedefi oluşturuluyor

Direct3D9 içeriğini bir içinde göstermek için bir D3DImage Direct3D9 yüzeyi oluşturup yöntemini çağırarak atayabilirsiniz SetBackBuffer .

Bağdaştırıcı desteği doğrulanıyor

Bir yüzey oluşturmadan önce, tüm bağdaştırıcıların gereken yüzey özelliklerini desteklediğini doğrulayın. Yalnızca bir bağdaştırıcıya İşleseniz bile, WPF penceresi sistemdeki herhangi bir bağdaştırıcıda görüntülenebilir. Birden çok bağdaştırıcılı yapılandırmayı işleyen her zaman Direct3D9 kodu yazmanız gerekir ve WPF, kullanılabilir bağdaştırıcılar arasında yüzeyi taşıyabileceğinden, destek için tüm bağdaştırıcıları denetlemeniz gerekir.

Aşağıdaki kod örneğinde, Direct3D9 desteği için sistemdeki tüm bağdaştırıcıların nasıl kontrol yapılacağı gösterilmektedir.

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

Yüzey oluşturma

Bir yüzey oluşturmadan önce, cihaz yeteneklerinin hedef işletim sisteminde iyi performansı desteklediğini doğrulayın. Daha fazla bilgi için bkz. Direct3D9 ve WPF birlikte çalışabilirliği Için performans konuları.

Cihaz yeteneklerini doğruladıysanız yüzeyi oluşturabilirsiniz. Aşağıdaki kod örneği, oluşturma hedefinin nasıl oluşturulacağını göstermektedir.

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

'Den

WDDM 'yi kullanmak üzere yapılandırılmış Windows Vista ve sonraki işletim sistemlerinde, işleme hedefi dokusu oluşturabilir ve düzey 0 yüzeyini SetBackBuffer yönteme geçirebilirsiniz. bu yaklaşım, Windows XP 'de önerilmez, çünkü bir kilitlenebilir işleme hedefi dokusu oluşturamazsınız ve performans azaltılabilir.

Cihaz durumunu işleme

D3DImageSınıfı, D3DImage ve ön arabellekolarak adlandırılan iki görüntüleme arabelleğini yönetir. Arka arabellek Direct3D yüzeyiniz. Yeniden arabellekte yapılan değişiklikler Unlock , yöntemini çağırdığınızda, donanımda görüntülendiği zaman ön arabelleğe kopyalanır. Bazen ön arabellek kullanılamaz hale gelir. Bu kullanılabilirlik eksikliği, ekran kilitleme, tam ekran özel Direct3D uygulamaları, Kullanıcı geçişi veya diğer sistem etkinliklerinden kaynaklanıyor olabilir. Bu durumda, WPF uygulamanız olay işlenerek bilgilendirilir IsFrontBufferAvailableChanged . Uygulamanızın ön arabelleğin kullanım dışı olmasına nasıl yanıt verdiği, WPF 'nin yazılım işlemeye geri dönebilmesi için etkin olup olmamasına bağlıdır. SetBackBufferYönteminde, WPF 'nin yazılım işlemeye geri dönüp çağırmayacağını belirten bir parametre alan aşırı yüklemesi vardır.

SetBackBuffer(D3DResourceType, IntPtr)Aşırı yüklemeyi çağırdığınızda veya SetBackBuffer(D3DResourceType, IntPtr, Boolean) parametresi olarak ayarlanmış bir aşırı yüklemeyi çağırdığınızda enableSoftwareFallbackfalse , ön arabellek kullanılamaz olduğunda ve hiçbir şey görüntülenmediğinde, işleme sistemi başvurusunu geri arabelleğe bırakır. Ön arabellek yeniden kullanılabilir olduğunda, işleme sistemi, IsFrontBufferAvailableChanged WPF uygulamanıza bildirimde bulunan olayı başlatır. IsFrontBufferAvailableChangedGeçerli bir Direct3D yüzeyine göre işlemeyi yeniden başlatmak için olay işleyicisi oluşturabilirsiniz. İşlemeyi yeniden başlatmak için çağrısı yapmanız gerekir SetBackBuffer .

SetBackBuffer(D3DResourceType, IntPtr, Boolean)enableSoftwareFallback ' İ ' olarak ayarlanan parametre ile aşırı yüklemeyi çağırdığınızda true , ön arabellek kullanılamaz hale geldiğinde işleme sistemi başvurusunu geri arabelleğe tutar, bu nedenle SetBackBuffer ön arabelleğin yeniden kullanılabilir olduğu durumlarda çağırmanız gerekmez.

Yazılım işleme etkinleştirildiğinde, Kullanıcı cihazının kullanılamaz hale geldiği, ancak işleme sisteminin Direct3D yüzeyine bir başvuruyu sakladığı durumlar olabilir. Direct3D9 cihazının kullanılamıyor olup olmadığını denetlemek için TestCooperativeLevel yöntemini çağırın. Direct3D9Ex cihazlarını denetlemek için yöntemini çağırın CheckDeviceState , çünkü TestCooperativeLevel Yöntem kullanım dışıdır ve her zaman başarılı sonucunu döndürür. Kullanıcı aygıtı kullanılamaz hale gelirse, SetBackBuffer WPF 'nin geri arabellek başvurusunu serbest bırakma çağrısına çağrı yapın. Cihazınızı sıfırlamanız gerekiyorsa, SetBackBufferbackBuffer olarak ayarlanmış parametresiyle çağırın null ve ardından SetBackBufferbackBuffer geçerli bir Direct3D yüzeyine ayarla ile yeniden çağırın.

ResetYalnızca çok bağdaştırıcılı destek uygularsanız, geçersiz bir cihazdan kurtarmak için yöntemi çağırın. Aksi takdirde, tüm Direct3D9 arabirimlerini serbest bırakın ve tamamen yeniden oluşturun. Bağdaştırıcı düzeni değiştiyse, değişiklikten önce oluşturulan Direct3D9 nesneleri bu nesneler güncellenmez.

Yeniden boyutlandırmayı işleme

A, D3DImage Yerel boyutundan farklı bir çözünürlükte görüntüleniyorsa, BitmapScalingMode için yerine geçen geçerli olan öğesine göre ölçeklendirilir BilinearFant .

Daha yüksek uygunluğa ihtiyacınız varsa, değişiklik boyutu kapsayıcısı olduğunda yeni bir yüzey oluşturmanız gerekir D3DImage .

Yeniden boyutlandırmayı işlemek için üç olası yaklaşım vardır.

  • Düzen sistemine katılın ve boyut değiştiğinde yeni bir yüzey oluşturun. Video belleğini tüketebilir veya parçalamayabilmeniz için çok fazla yüzey oluşturmayın.

  • Yeni yüzeyi oluşturmak için sabit bir süre için yeniden boyutlandırma olayı gerçekleşene kadar bekleyin.

  • DispatcherTimerSaniye başına kapsayıcı boyutlarını birkaç kez denetleyen bir oluştur.

Çoklu izleyici Iyileştirmesi

İşleme sistemi bir diğer monitöre taşınırsa performansı önemli ölçüde düşürür D3DImage .

WDDM 'de, izleyiciler aynı video kartında olduğu ve kullandığınız sürece Direct3DCreate9Ex performansta bir azaltma yoktur. İzleyiciler ayrı video kartlarında ise, performans azalır. Windows XP 'de performans her zaman azalır.

D3DImageBaşka bir monitöre taşırken, iyi performansı geri yüklemek için ilgili bağdaştırıcıda yeni bir yüzey oluşturabilirsiniz.

Performans cezasından kaçınmak için, özellikle birden çok monitör örneğine kod yazın. Aşağıdaki listede, çok monitörle kod yazmanın bir yolu gösterilmektedir.

  1. D3DImageYöntemi ile birlikte ekran alanının bir noktasını bulun Visual.ProjectToScreen .

  2. MonitorFromPointNoktayı görüntüleyen izleyiciyi bulmak için GDI yöntemini kullanın.

  3. IDirect3D9::GetAdapterMonitorİzleyicinin hangi Direct3D9 bağdaştırıcısı üzerinde olduğunu bulmak için yöntemini kullanın.

  4. Bağdaştırıcı, arka arabelleğin bulunduğu bağdaştırıcıyla aynı değilse, yeni monitörde yeni bir geri arabellek oluşturun ve D3DImage geri arabelleğe atayın.

Not

D3DImageKuruluşunda, WDDM ve aynı bağdaştırıcı üzerinde olması dışında, performans yavaş olur IDirect3D9Ex . Bu durumda performansı geliştirmenin bir yolu yoktur.

Aşağıdaki kod örneği, geçerli izleyicinin nasıl bulunacağını gösterir.

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

D3DImageKapsayıcının boyutu veya konumu değiştiğinde izleyiciyi güncelleştirin veya bir DispatcherTimer saniyede birkaç kez güncelleştiren bir güncelleştirme kullanarak izleyiciyi güncelleştirin.

WPF yazılım Işleme

WPF, aşağıdaki durumlarda yazılımda Kullanıcı arabirimi iş parçacığında zaman uyumlu olarak işler.

Bu durumlardan biri gerçekleştiğinde, işleme sistemi CopyBackBuffer donanım arabelleğini yazılıma kopyalamak için yöntemini çağırır. Varsayılan uygulama, GetRenderTargetData yüzeyiniz ile yöntemini çağırır. Bu çağrı kilit/kilit açma deseninin dışında gerçekleştiğinden başarısız olabilir. Bu durumda, CopyBackBuffer yöntemi döndürür null ve görüntü gösterilmez.

Yöntemi geçersiz kılabilir CopyBackBuffer , temel uygulamayı çağırabilir ve döndürürse, null bir yer tutucu döndürebilirsiniz BitmapSource .

Temel uygulamayı çağırmak yerine kendi yazılım işlelerinizi de uygulayabilirsiniz.

Not

WPF, yazılımda tamamen işlenirse, D3DImage WPF 'in ön arabelleği olmadığından gösterilmez.

Ayrıca bkz.