Aracılığıyla paylaş


WPF ve Direct3D9 Birlikte Çalışması

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

Dekont

WPF'de Direct3D9 içeriğini kullanırken performans hakkında da düşünmeniz gerekir. Performansı iyileştirme hakkında daha fazla bilgi için bkz . Direct3D9 ve WPF Birlikte Çalışabilirliği için PerformansLa İlgili Önemli Noktalar.

Arabellekleri Görüntüle

D3DImage sınıfı, arka arabellek ve ön arabellek olarak adlandırılan iki görüntüleme arabelleği yönetir. Arka tampon, Direct3D9 yüzeyinizdir. Arka arabellekte yapılan değişiklikler, yöntemini çağırdığınızda Unlock ön arabelleğe iletilir.

Aşağıdaki çizimde arka arabellek ile ön arabellek 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 IDirect3D9 oluşturmak için kullanabileceğiniz iki Direct3D9 nesnesi vardır ve IDirect3D9Ex. Ve cihazlarını oluşturmak IDirect3DDevice9IDirect3DDevice9Ex için sırasıyla bu nesneleri kullanın.

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, Windows Görüntü Sürücüsü Modeli'ni (WDDM) kullanmak üzere yapılandırılmış bir ekranla yöntemini kullanın Direct3DCreate9Ex . Direct3DCreate9 yöntemini başka bir platformda kullanın.

Direct3DCreate9Ex yönteminin kullanılabilirliği

d3d9.dll yalnızca Windows Vista veya sonraki işletim sistemlerinde yöntemine Direct3DCreate9Ex sahiptir. Windows XP'de işlevi doğrudan bağlarsanız uygulamanız yüklenemez. Yöntemin Direct3DCreate9Ex desteklenip desteklenmediğini belirlemek için DLL'yi yükleyin ve proc adresini arayın. Aşağıdaki kod, yönteminin nasıl test yapılacağını Direct3DCreate9Ex gösterir. Tam kod örneği için bkz . İzlenecek Yol: WPF'de Barındırma için Direct3D9 İç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

Cihaz oluşturmak için HWND gerekir. Genel olarak, Direct3D9'un kullanması için sahte bir HWND oluşturursunuz. Aşağıdaki kod örneği, sahte bir HWND'nin nasıl oluşturulacağını 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;
}

Parametreleri Sun

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

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

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

Aşağıdaki kod, yapının nasıl başlatılmış olduğunu D3DPRESENT_PARAMETERS gösterir.

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 İşleme Hedefi Oluşturma

içinde D3DImageDirect3D9 içeriğini görüntülemek için bir Direct3D9 yüzeyi oluşturur ve yöntemini çağırarak SetBackBuffer atarsınız.

Bağdaştırıcı Desteğini Doğrulama

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

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

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

Surface'i oluşturma

Yüzey oluşturmadan önce, cihaz özelliklerinin 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 özelliklerini doğruladığınızda, yüzeyi oluşturabilirsiniz. Aşağıdaki kod örneği, işleme hedefinin nasıl oluşturulacağını gösterir.

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

WDDM kullanmak üzere yapılandırılan Windows Vista ve sonraki işletim sistemlerinde bir işleme hedef dokusu oluşturabilir ve düzey 0 yüzeyini yöntemine SetBackBuffer geçirebilirsiniz. Bu yaklaşım Windows XP'de önerilmez, çünkü kilitlenebilir bir işleme hedef dokusu oluşturamazsınız ve performans azalır.

Cihaz Durumunu İşleme

D3DImage sınıfı, arka arabellek ve ön arabellek olarak adlandırılan iki görüntüleme arabelleği yönetir. Arka tampon, Direct3D yüzeyinizdir. Yöntemi çağırdığınızda Unlock arka arabellekte yapılan değişiklikler ön arabelleğe kopyalanır ve burada donanımda görüntülenir. Bazen ön arabellek kullanılamaz duruma gelir. Bu kullanılabilirlik eksikliğine ekran kilitleme, tam ekran özel Direct3D uygulamaları, kullanıcı değiştirme veya diğer sistem etkinlikleri neden olabilir. Bu durum oluştuğunda, OLAYı işleyerek IsFrontBufferAvailableChanged WPF uygulamanıza bildirim gönderilir. Uygulamanızın ön arabelleğin kullanılamaz duruma gelmesine nasıl yanıt verdiği WPF'nin yazılım işlemeye geri dönmek için etkinleştirilip etkinleştirilmediğine bağlıdır. yöntemi, SetBackBuffer WPF'nin yazılım işlemeye geri dönüp dönmediğini belirten bir parametre alan bir aşırı yüklemeye sahiptir.

Aşırı yüklemeyi çağırdığınızda SetBackBuffer(D3DResourceType, IntPtr) veya parametresi olarak ayarlandığında falseaşırı yüklemeyi enableSoftwareFallback çağırdığınızdaSetBackBuffer(D3DResourceType, IntPtr, Boolean), ön arabellek kullanılamaz hale geldiğinde ve hiçbir şey görüntülenmediğinde işleme sistemi arka arabelleğe başvurusunu serbest bırakır. Ön arabellek yeniden kullanılabilir olduğunda, işleme sistemi WPF uygulamanıza bildirmek için olayı tetikler IsFrontBufferAvailableChanged . Geçerli bir Direct3D yüzeyiyle işlemeyi IsFrontBufferAvailableChanged yeniden başlatmak için olay işleyicisi oluşturabilirsiniz. İşlemeyi yeniden başlatmak için öğesini çağırmalısınız SetBackBuffer.

parametresi olarak ayarlanmış trueaşırı yüklemeyi enableSoftwareFallback çağırdığınızdaSetBackBuffer(D3DResourceType, IntPtr, Boolean), ön arabellek kullanılamaz duruma geldiğinde işleme sistemi arka arabelleğe başvurusunu korur, bu nedenle ön arabellek yeniden kullanılabilir olduğunda çağrı SetBackBuffer yapmanız gerekmez.

Yazılım işleme etkinleştirildiğinde, kullanıcının cihazının kullanılamaz duruma geldiği ancak işleme sisteminin Direct3D yüzeyine başvuruyu koruduğu durumlar olabilir. Direct3D9 cihazının kullanılamıyor olup olmadığını denetlemek için yöntemini çağırın TestCooperativeLevel . 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ı döndürür. Kullanıcı cihazı kullanılamaz duruma geldiyse WPF'nin arka arabelleğe başvuruyu serbest bırakmak için çağrısı SetBackBuffer yapın. Cihazınızı sıfırlamanız gerekiyorsa parametresi olarak ayarlanmış nullolarak çağrısı SetBackBufferbackBuffer yapın ve ardından geçerli bir Direct3D yüzeyine ayarlanmış olarak yeniden backBuffer çağırınSetBackBuffer.

Reset Yalnızca çok bağdaştırıcılı destek uyguladığınızda geçersiz bir cihazdan kurtarmak için yöntemini ç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şiklik öncesinde oluşturulan Direct3D9 nesneleri güncelleştirilmez.

Yeniden Boyutlandırmayı İşleme

bir D3DImage yerel boyutu dışında bir çözünürlükte görüntülenirse, geçerli öğesine göre BitmapScalingModeölçeklendirilir, ancak yerine Bilinear değerinin olması gerekir Fant.

Daha yüksek aslına uygunluk gerekiyorsa, boyutu değişen kapsayıcıda D3DImage yeni bir yüzey oluşturmanız gerekir.

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üketebileceği veya parçaladığı 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ının gerçekleşmemesini bekleyin.

  • Kapsayıcı boyutlarını saniyede birkaç kez denetleen bir DispatcherTimer oluşturun.

Çok monitörlü İyileştirme

İşleme sistemi bir monitörü başka bir D3DImage monitöre taşırken önemli ölçüde düşük performans elde edilebilir.

WDDM'de monitörler aynı ekran kartında olduğu ve kullandığınız Direct3DCreate9Exsürece performansta bir azalma olmaz. Monitörler ayrı ekran kartlarındaysa performans azalır. Windows XP'de performans her zaman azalır.

Başka bir monitöre geçtiğinde D3DImage , 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 çok monitörlü durum için kod yazın. Aşağıdaki listede çok monitörlü kod yazmanın bir yolu gösterilmektedir.

  1. yöntemiyle Visual.ProjectToScreen ekran alanının bir noktasını D3DImage bulun.

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

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

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

Dekont

D3DImage Bu durumda, WDDM ve IDirect3D9Ex aynı bağdaştırıcının durumu dışında performans yavaş olacaktır. Bu durumda performansı artırmanın bir yolu yoktur.

Aşağıdaki kod örneğinde geçerli izleyicinin nasıl bulunu olduğu gösterilmektedir.

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

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

WPF Yazılım İşleme

WPF, aşağıdaki durumlarda yazılımdaki ui iş parçacığında zaman uyumlu olarak işlenir.

Bu durumlardan biri oluştuğunda, işleme sistemi donanım arabelleği yazılıma kopyalamak için yöntemini çağırır CopyBackBuffer . Varsayılan uygulama, surface'ınız ile yöntemini çağırır GetRenderTargetData . Bu çağrı Kilit/Kilit Açma düzeninin dışında gerçekleştiğinden başarısız olabilir. Bu durumda yöntemi CopyBackBuffer döndürür null ve görüntü görüntülenmez.

yöntemini geçersiz kılabilir CopyBackBuffer , temel uygulamayı çağırabilir ve döndürürse nullbir yer tutucu BitmapSourcedöndürebilirsiniz.

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

Dekont

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

Ayrıca bkz.