Megosztás a következőn keresztül:


WPF és Direct3D9 együttműködés

A Direct3D9-tartalmat a Windows Presentation Foundation (WPF) alkalmazásba is belefoglalhatja. Ez a témakör azt ismerteti, hogyan hozhat létre Direct3D9-tartalmat, hogy az hatékonyan működjön együtt a WPF-vel.

Megjegyzés:

Ha Direct3D9-tartalmat használ a WPF-ben, a teljesítményre is gondolnia kell. A teljesítmény optimalizálásával kapcsolatos további információkért lásd Direct3D9 és WPF együttműködésiteljesítményével kapcsolatos szempontokat.

Pufferek megjelenítése

A D3DImage osztály két megjelenítési puffert kezel, amelyek az háttér-puffer és az előlap-puffer. A hátsó puffer a Direct3D9 felület. A rendszer a Unlock metódus meghívásakor a háttérpuffer módosításait az első pufferbe másolja.

Az alábbi ábra a háttérpuffer és az első puffer közötti kapcsolatot mutatja be.

D3DImage kijelzőpufferek

Direct3D9-eszköz létrehozása

A Direct3D9-tartalmak megjelenítéséhez létre kell hoznia egy Direct3D9-eszközt. Két Direct3D9-objektumot használhat eszköz, IDirect3D9 és IDirect3D9Exlétrehozásához. Ezekkel az objektumokkal IDirect3DDevice9 és IDirect3DDevice9Ex eszközöket hozhat létre.

Hozzon létre egy eszközt az alábbi módszerek egyikének meghívásával.

  • IDirect3D9 * Direct3DCreate9(UINT SDKVersion);

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

Windows Vista vagy újabb operációs rendszeren használja a Direct3DCreate9Ex metódust a Windows megjelenítési illesztőprogram-modell (WDDM) használatára konfigurált kijelzővel. Használja a Direct3DCreate9 metódust bármely más platformon.

A Direct3DCreate9Ex metódus elérhetősége

A d3d9.dll Direct3DCreate9Ex metódus csak Windows Vista vagy újabb operációs rendszeren használható. Ha közvetlenül összekapcsolja a függvényt Windows XP rendszeren, az alkalmazás nem töltődik be. Annak megállapításához, hogy a Direct3DCreate9Ex metódus támogatott-e, töltse be a DLL-t, és keresse meg a proc címet. Az alábbi kód bemutatja, hogyan tesztelheti a Direct3DCreate9Ex metódust. A teljes példakódért tekintse meg a útmutatót: Direct3D9 tartalom létrehozása és hosztolása a WPF-ben.

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 létrehozása

Az eszköz létrehozásához HWND szükséges. Általában létrehoz egy ál-HWND-t, hogy a Direct3D9 használhassa. Az alábbi példakód bemutatja, hogyan hozhat létre hamis HWND-t.

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

Paraméterek bemutatása

Az eszköz létrehozásához D3DPRESENT_PARAMETERS szerkezetre is szükség van, de csak néhány paraméter fontos. Ezek a paraméterek a memóriaigény minimalizálása érdekében vannak kiválasztva.

Állítsa a BackBufferHeight és BackBufferWidth mezőket 1 értékre. Ha 0-ra állítja őket, akkor azokat a HWND méreteihez igazítják.

Mindig állítsa be a D3DCREATE_MULTITHREADED és D3DCREATE_FPU_PRESERVE jelzőket, hogy megakadályozza a Direct3D9 által használt memória sérülését, és megakadályozza, hogy a Direct3D9 módosítsa az FPU-beállításokat.

Az alábbi kód bemutatja, hogyan inicializálhatja a D3DPRESENT_PARAMETERS szerkezetet.

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

A háttérpuffer renderelési céljának létrehozása

Ha Direct3D9-tartalmat szeretne megjeleníteni egy D3DImage, hozzon létre egy Direct3D9 felületet, és rendelje hozzá a SetBackBuffer metódus meghívásával.

Az adapter támogatásának ellenőrzése

Mielőtt létrehoz egy felületet, ellenőrizze, hogy az összes adapter támogatja-e a szükséges felületi tulajdonságokat. Még ha csak egy adapterre is renderel, előfordulhat, hogy a WPF ablak a rendszer bármely adapterén megjelenik. Mindig olyan Direct3D9-kódot kell írnia, amely kezeli a többadapteres konfigurációkat, és ellenőriznie kell az összes adapter támogatását, mert a WPF áthelyezheti a felületet a rendelkezésre álló adapterek között.

Az alábbi példakód bemutatja, hogyan ellenőrizheti a rendszer összes adapterét a Direct3D9-támogatáshoz.

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 Surface létrehozása

A felület létrehozása előtt ellenőrizze, hogy az eszköz képességei támogatják-e a megfelelő teljesítményt a cél operációs rendszeren. További információkért lásd: A Direct3D9 és a WPF interoperabilitási teljesítményszempontjai.

Ha igazolt eszközképességekkel rendelkezik, létrehozhatja a felületet. Az alábbi példakód bemutatja, hogyan hozható létre a renderelési cél.

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

A WDDM használatára konfigurált Windows Vista és újabb operációs rendszereken létrehozhat egy renderelési célmintát, és átadhatja a 0. szintű felületet a SetBackBuffer metódusnak. Ez a módszer Windows XP rendszeren nem ajánlott, mert nem lehet zárolható renderelési célmintát létrehozni, és a teljesítmény csökken.

Eszközállapot kezelése

A D3DImage osztály két megjelenítési puffert kezel, amelyek az háttér-puffer és az előlap-puffer. Ez a hátsó puffer az Ön Direct3D felülete. A háttérpuffer módosításait a rendszer a Unlock metódus meghívásakor az első pufferbe másolja, ahol az megjelenik a hardveren. Időnként az első puffer elérhetetlenné válik. A rendelkezésre állás hiányát okozhatja a képernyőzárolás, a teljes képernyős exkluzív Direct3D-alkalmazások, a felhasználóváltás vagy más rendszertevékenységek. Ha ez történik, a WPF-alkalmazás értesítést kap a IsFrontBufferAvailableChanged esemény kezelésével. Az, hogy az alkalmazás hogyan reagál az első puffer elérhetetlenné válására, attól függ, hogy a WPF engedélyezve van-e a szoftveres renderelésre való visszalépéshez. A SetBackBuffer metódus túlterheléssel rendelkezik, amely egy paramétert használ, amely meghatározza, hogy a WPF visszaesik-e a szoftveres renderelésre.

Amikor a SetBackBuffer(D3DResourceType, IntPtr) túlterhelést hívja meg, vagy a SetBackBuffer(D3DResourceType, IntPtr, Boolean) túlterhelést a enableSoftwareFallback paraméter falseértékre állítja, a renderelő rendszer a háttérpufferre mutató hivatkozást bocsátja ki, amikor az első puffer elérhetetlenné válik, és semmi sem jelenik meg. Ha az első puffer ismét elérhető, a renderelő rendszer a IsFrontBufferAvailableChanged eseményt aktiválja, amely értesíti a WPF-alkalmazást. Létrehozhat egy eseménykezelőt a IsFrontBufferAvailableChanged eseményhez, hogy újrainduljon a renderelés egy érvényes Direct3D-felülettel. A renderelés újraindításához meg kell hívnia SetBackBuffer.

Amikor a SetBackBuffer(D3DResourceType, IntPtr, Boolean) túlterhelést a enableSoftwareFallback paraméter trueértékre állítja, a renderelő rendszer megőrzi a háttérpufferre mutató hivatkozását, amikor az első puffer elérhetetlenné válik, így nem kell meghívni SetBackBuffer, ha az első puffer ismét elérhető.

Ha engedélyezve van a szoftveres renderelés, előfordulhat, hogy a felhasználó eszköze elérhetetlenné válik, de a renderelő rendszer megőrzi a Direct3D felületre mutató hivatkozást. Ha ellenőrizni szeretné, hogy egy Direct3D9-eszköz nem érhető-e el, hívja meg a TestCooperativeLevel metódust. Direct3D9Ex-eszközök ellenőrzéséhez hívja meg a CheckDeviceState metódust, mert a TestCooperativeLevel metódus elavult, és mindig sikert ad vissza. Ha a felhasználói eszköz elérhetetlenné vált, hívja fel a SetBackBuffer, hogy engedje fel a WPF-nek a háttérpufferre mutató hivatkozását. Ha alaphelyzetbe kell állítania az eszközt, hívja meg a SetBackBuffer parancsot a backBuffer paramétert null-re állítva, majd hívja meg ismét a SetBackBuffer-at, és állítsa a backBuffer-et egy érvényes Direct3D felületre.

Hívja meg a Reset metódust az érvénytelen eszközről való helyreállításhoz, csak akkor, ha többadapteres támogatást valósít meg. Máskülönben engedje fel az összes Direct3D9-felületet, és hozza létre őket teljes mértékben újra. Ha az adapter elrendezése megváltozott, a módosítás előtt létrehozott Direct3D9-objektumok nem frissülnek.

Átméretezés kezelése

Ha egy D3DImage a natív méretétől eltérő felbontásban jelenik meg, akkor a jelenlegi BitmapScalingModeszerint skálázódik, azzal a kivételével, hogy a BilinearFanthelyettesít.

Ha nagyobb hűségre van szüksége, új felületet kell létrehoznia, amikor a D3DImage tároló mérete megváltozik.

Három lehetséges módszer van az átméretezés kezelésére.

  • Vegyen részt az elrendezési rendszerben, és hozzon létre egy új felületet a méret változásakor. Ne hozzon létre túl sok felületet, mert a videomemória elfogyhat vagy töredezett lehet.

  • Várjon addig, amíg egy adott időszakig nem következik be átméretezési esemény, hogy létrehozhassa az új felületet.

  • Hozzon létre egy DispatcherTimer, amely másodpercenként többször ellenőrzi a tárolódimenziókat.

Többmonitoros optimalizálás

A teljesítmény jelentősen csökkenhet, ha a renderelő rendszer áthelyez egy D3DImage egy másik monitorra.

A WDDM esetén, amíg a monitorok ugyanazon a videokártyán vannak, és a Direct3DCreate9Ex-t használja, a teljesítmény nem csökken. Ha a monitorok külön videokártyákon vannak, a teljesítmény csökken. Windows XP rendszeren a teljesítmény mindig csökken.

Amikor a D3DImage másik monitorra kerül, létrehozhat egy új felületet a megfelelő adapteren a jó teljesítmény visszaállításához.

A teljesítménybírság elkerülése érdekében írjon kódot kifejezetten a többmonitoros esethez. Az alábbi lista egy többmonitoros kód írásának egyik módját mutatja be.

  1. Keresse meg a D3DImage egy pontját a képernyőtérben a Visual.ProjectToScreen metódussal.

  2. A MonitorFromPoint GDI metódussal keresse meg a pontot megjelenítő monitort.

  3. A IDirect3D9::GetAdapterMonitor metódus használatával állapítsa meg, hogy melyik Direct3D9-adapteren van a monitor.

  4. Ha az adapter nem ugyanaz, mint a háttérpufferrel rendelkező adapter, hozzon létre egy új háttérpuffert az új monitoron, és rendelje hozzá a D3DImage háttérpufferhez.

Megjegyzés:

Ha a D3DImage monitorokat hidal át, a teljesítmény lassú lesz, kivéve, ha a WDDM és a IDirect3D9Ex ugyanazon az adapteren van. Ebben a helyzetben nincs mód a teljesítmény javítására.

Az alábbi példakód bemutatja, hogyan keresheti meg az aktuális figyelőt.

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

Frissítse a figyelőt, amikor a D3DImage tároló mérete vagy pozíciója megváltozik, vagy frissítse a figyelőt egy másodpercenként néhányszor frissülő DispatcherTimer használatával.

WPF szoftveres renderelés

A WPF szinkron módon jeleníti meg a felhasználói felület szálát a szoftverben az alábbi helyzetekben.

Az ilyen helyzetek egyike esetén a renderelő rendszer meghívja a CopyBackBuffer metódust a hardverpuffer szoftverbe másolásához. Az alapértelmezett implementáció meghívja a GetRenderTargetData metódust a felülettel. Mivel ez a hívás a zárolás/feloldás mintán kívül történik, ezért sikertelen lehet. Ebben az esetben a CopyBackBuffer metódus null ad vissza, és nem jelenik meg kép.

Felülbírálhatja a CopyBackBuffer metódust, meghívhatja az alapimplementációt, és ha az null-et adja vissza, visszaadhat egy BitmapSourcehelyőrzőt.

Saját szoftverleképezést is implementálhat ahelyett, hogy meghívná az alap implementációt.

Megjegyzés:

Ha a WPF teljes mértékben a szoftverben jelenik meg, D3DImage nem jelenik meg, mert a WPF nem rendelkezik előtérpufferrel.

Lásd még