إنشاء محتوى Direct3D9 التي يمكن استضافته في WPF

يمكن أن تضمن محتوى Direct3D9 في تطبيق Windows Presentation Foundation (WPF). يصف هذا الموضوع كيفية إنشاء محتوى Direct3D9 بحيث يشتغل بكفاءة مع WPF.

ملاحظةملاحظة

عند استخدام محتوى Direct3D9 في WPF ، تحتاج أيضاً إلى التفكير حول الأداء.لمزيد من المعلومات حول كيفية تحسين الأداء، راجع اعتبارات الأداء ل Direct3D9 و إمكانية التشغيل التفاعلى ل WPF.

مخازن العرض المؤقتة

فئة D3DImage تدير مخزنين مؤقتين للعرض، والتي تدعى المخزن المؤقت الخلفي و المخزن المؤقت الأمامي . المخزن المؤقت الخلفي هو سطح Direct3D9 الخاص بك. التغييرات إلى المخزن المؤقت الخلفي يتم نسخها إلى المخزن المؤقت الأمامي عند استدعاء أسلوب Unlock.

يبين الرسم التوضيحي التالي العلاقة بين المخزن المؤقت الخلفي و المخزن المؤقت الأمامي.

المخازن المؤقتة لعرض D3DImage

إنشاء جهاز Direct3D9

لتقديم محتوي Direct3D9 يجب أن تقوم بإنشاء جهاز Direct3D9. يوجد كائنين Direct3D9 يمكنك استخدامهم لإنشاء الجهاز ، IDirect3D9 و IDirect3D9Ex. استخدم هذه الكائنات لإنشاء أجهزة IDirect3DDevice9 و IDirect3DDevice9Ex ، على الترتيب.

إنشاء جهاز عن طريق استدعاء إحدى الطرق التالية.

  • IDirect3D9 * Direct3DCreate9(UINT SDKVersion);

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

استخدم أسلوب Direct3DCreate9Ex على نظام التشغيل Windows Vista مع عرض تم تكوينه لاستخدام طراز برنامج تشغيل عرض windows (WDDM). استخدم أسلوب Direct3DCreate9 على أي نظام أساسي أخر.

توفر أسلوب Direct3DCreate9Ex

فقط d3d9.dll على نظام التشغيل Windows Vista لديه أسلوب Direct3DCreate9Ex. إذا قمت بربط الدالة مباشرة على Windows XP ، التطبيق الخاص بك سيفشل في التحميل. لتحديد ما إذا كان أسلوب Direct3DCreate9Ex مُدعم, قم بتحميل مكتبة الارتباط الحيوي (DLL) وابحث عن عنوان proc. التعليمة البرمجية التالية توضح كيفية الاختبار لأسلوب Direct3DCreate9Ex. إذا أردت استعراض مثال تعليمات برمجية كامل , انظر الإرشادات التفصيلية: إنشاء محتوى Direct3D9 للاستضافة في 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;
}

إنشاء HWND

إنشاء جهاز يتطلب HWND. بشكل عام، قم بإنشاء HWND وهمي حتى يستخدمه Direct3D9. يوضح مثال التعليمة البرمجية التالي كيفية إنشاء HWDN وهمي.

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

معلمات present

إنشاء جهاز يتطلب أيضاً بنية D3DPRESENT_PARAMETERS و لكن معلمات قليلة فقط هامة. تم اختيار هذه المعلمات لتقليل أثر الذاكرة.

التعيين BackBufferHeightو BackBufferWidthحقول إلى 1. يؤدي تعيين هذه إلى 0 إلى لهم بتعيين الأبعاد HWND.

دائماً قم بتعيين إشارات D3DCREATE_MULTITHREADED و D3DCREATE_FPU_PRESERVE لمنع إتلاف الذاكرة المستخدمة من قبل Direct3D9 و منع Direct3D9 من تغيير إعدادات FPU.

التعليمة البرمجية التالية توضح كيفية تهيئة بنية 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;
}

إنشاء هدف عرض مخزن المؤقت الخلفي

لعرض محتوى Direct3D9 في D3DImage ، قم بإنشاء سطح Direct3D9 و تعيينها عن طريق استدعاء أسلوب SetBackBuffer.

التحقق من دعم المحول

قبل إنشاء سطح ، تحقق من دعم كافة المحولات لخصائص السطح التي تتطلبها. حتى في حالة التقديم إلى محول واحد فقط صواب قد يتم عرض الإطار على أي محول في النظام. يجب كتابة التعليمات البرمجية Direct3D9 التي تعالج تكوينات multi-adapter و يجب عليك التحقق من كافة المحولات للحصول على دعم لأن WPF قد ينقل السطح بين المحولات المتوفرة.

مثال التعليمة البرمجية التالي يوضح كيفية التحقق من كافة المحولات على النظام للحصول على دعم 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;
}

إنشاء سطح

قبل إنشاء سطح ، تحقق من أن قدرات الجهاز تدعم أداءً جيداً على نظام تشغيل الهدف. لمزيد من المعلومات، راجع اعتبارات الأداء ل Direct3D9 و إمكانية التشغيل التفاعلى ل WPF.

عند التحقق من قدرات الجهاز يمكنك إنشاء السطح. مثال التعليمة البرمجية التالي يوضح كيفية إنشاء هدف تقديم.

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

على نظام التشغيل Windows Vista الذي تم تكوينه على استخدام WDDM، يمكنك إنشاء مادة هدف التقديم و تمرير سطح المستوى 0 إلى أسلوب SetBackBuffer. يعتبر هذا الأسلوب ليس مستحسن على Windows XP، لأن لا يمكنك إنشاء مادة هدف تقديم مقفل و سيتم تقليل الأداء.

معالجة حالة الجهاز

عند انتقالات خاصية IsFrontBufferAvailable من true إلى false، WPF لا يعرض D3DImage و لا ينسخ المخزن المؤقت الخلفي الخاص بك إلى المخزن المؤقت الأمامي. وهذا يعني عادة أنه تم فقد الجهاز.

عند فقدان الجهاز يجب إيقاف تقديم التعليمات البرمجية و انتظر من خاصية IsFrontBufferAvailable الانتقال إلى true. معالجة حدث IsFrontBufferAvailableChanged لإعلامك بهذا الانتقال.

عندما تنتقل خاصية IsFrontBufferAvailable من false إلى true ، ينبغي عليك التحقق من جهازك لتحديد ما إذا كان صحيح أم لا.

لأجهزة Direct3D9 قم باستدعاء أسلوب TestCooperativeLevel. لأجهزة Direct3D9Ex قم باستدعاء أسلوب CheckDeviceState, لأن أسلوب TestCooperativeLevel تم إهماله و يقوم دوماً بإرجاع نجاح.

إذا كان الجهاز صالح قم باستدعاء أسلوب SetBackBuffer مرة أخرى مع السطح الأصلي.

إذا كان الجهاز غير صحيح, يجب إعادة تعيين الجهاز ثم إعادة-إنشاء الموارد. استدعاء أسلوب SetBackBuffer مع سطح من جهاز غير صالح يرفع استثناء.

استدعاء أسلوب Reset للاسترداد من جهاز غير صحيح فقط إذا قمت بتطبيق دعم multi-adapter. وإلا، قم بتحرير كافة واجهات Direct3D9 و إعادة-إنشاءهم بشكل كامل. إذا كان تم تغيير تخطيط المحول، لا يتم تحديث كائنات Direct3D9 التي تم إنشاؤها قبل التغيير.

معالجة تغيير الحجم

إذا كان D3DImage يعرض بدقة غير حجمه الأصلي فإنه يتم قياسه وفقاً إلى BitmapScalingMode الحالي ، إلا أنه يتم استبدال Bilinear ب Fant.

إذا تطلب دقة أعلى يجب عليك إنشاء سطح جديد عندما تغيير حاوية D3DImage الحجم.

هناك ثلاثة أساليب محتملة لمعالجة تغيير الحجم.

  • المشاركة في نظام التخطيط و إنشاء سطح جديد عند تغيير الحجم. لا تنشئ أسطح كثيرة جداً لأنه قد يستنفذ أو يقسم ذاكرة الفيديو.

  • انتظر حتى تأكد من أن حدث تغيير حجم لم يحدث لفترة زمنية ثابتة لإنشاء سطح جديد.

  • إنشاء DispatcherTimer الذي يتحقق من أبعاد الحاوية عدة مرات في الثانية.

أمثلية multi-monitor

يمكن أن ينتج أداء وظيفي منخفض بشكل ملحوظ عندما ينقل نظام العرض D3DImage إلى جهاز عرض آخر.

على WDDM، طالما أن أجهزة العرض على نفس بطاقة الفيديو و انت تستخدم Direct3DCreate9Ex، لا يوجد تقليل في الأداء. إذا كانت أجهزة العرض على بطاقات فيديو منفصلة, الأداء ينخفض. على Windows XP، يتم تقليل الأداء دوماً.

عندما D3DImage ينتقل إلى جهاز عرض آخر, يمكنك إنشاء سطح جديد على المحول المطابق لاستعادة أداء جيد.

لتجنب جزاء الأداء, اكتب التعليمات البرمجية خصيصاً لحالة multi-monitor. تعرض القائمة التالية طريقة واحدة لكتابة التعليمات البرمجية ل multi-monitor.

  1. اعثر على نقطة من D3DImage في مساحة الشاشة بأسلوب Visual.ProjectToScreen.

  2. استخدم أسلوب GDI MonitorFromPoint لإيجاد جهاز العرض الذي يعرض النقطة.

  3. استخدم أسلوب IDirect3D9::GetAdapterMonitor للبحث عن أي محول Direct3D9 عليه جهاز العرض.

  4. إذا كان المحول ليس نفس المحول مع المخزن المؤقت الخلفي, قم بإنشاء مخزن مؤقت خلفي جديد على جهاز العرض الجديد و قم بتعيينه إلى المخزن المؤقت الخلفي D3DImage.

ملاحظةملاحظة

إذا كان D3DImage straddlesأجهزة العرض, الأداء سيكون بطيئ، فيما عدا في حالة WDDM و IDirect3D9Ex على نفس المحول.لا توجد طريقة لتحسين الأداء في هذا الموقف.

يظهر المثال التعليمات البرمجية التالي كيفية البحث عن جهاز عرض حالي.

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

تحديث جهاز العرض عند تغيير حجم أو موضع حاوية D3DImage، أو تحديث جهاز العرض باستخدام DispatcherTimer التي تقوم بالتحديث عدة مرات في الثانية.

عرض البرامج WPF

ًWPF يعرض بشكل متزامن في مؤشر ترابط UI في البرنامج في الحالات التالية.

عند حدوث إحدى هذه الحالات يقوم نظام التقديم باستدعاء أسلوب CopyBackBuffer لنسخ المخزن المؤقت للأجهزة إلى البرنامج. التطبيق الافتراضي يستدعي أسلوب GetRenderTargetData مع السطح الخاص بك. لأن هذا الاستدعاء يحدث خارج نمط تأمين/إلغاء تأمين, فقد يفشل ذلك. في هذه الحالة، أسلوبCopyBackBuffer يقوم بإرجاع null و لا يتم عرض الصورة.

يمكنك تجاوز أسلوب CopyBackBuffer, استدعى تطبيق الأساس و إذا قامت بإرجاع null ، يمكنك إرجاع عنصر النائب BitmapSource.

يمكنك أيضاً تنفيذ عرض البرامج الخاصة بك بدلاً من استدعاء التطبيق الأساسي.

ملاحظةملاحظة

إذا كان WPF يعرض بشكل كامل في البرامج, D3DImage لا يظهر لأنه WPF ليس لديه المخزن المؤقت الأمامي.

راجع أيضًا:

المهام

الإرشادات التفصيلية: إنشاء محتوى Direct3D9 للاستضافة في WPF

الإرشادات التفصيلية: استضافة محتوى Direct3D9 في WPF

المرجع

D3DImage

المبادئ

اعتبارات الأداء ل Direct3D9 و إمكانية التشغيل التفاعلى ل WPF