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

تُظهر هذه الإرشادات التفصيلية كيفية استضافة محتوى Direct3D9 في تطبيق Windows Presentation Foundation (WPF).

في هذه الإرشادات التفصيلية, تؤدى المهام التالية:

  • قم بإنشاء مشروع WPF لاستضافة محتوى الـ Direct3D9.

  • استيراد المحتوى Direct3D9.

  • عرض محتوى Direct3D9 باستخدام الفئة D3DImage.

عندما تنتهي، سوف تعرف كيفية استضافة محتوى Direct3D9 في تطبيق WPF.

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

قد تختلف مربعات الحوار وأوامر القائمة التى تشاهدها الان عن تلك الموصوفة في التعليمات اعتماداً على الإعدادات النشطة أو الإصدار الخاص بك.لتغيير الإعدادات الخاصة بك, اختر إعدادات الاستيراد و التصدير ضمن القائمة أدوات .لمزيد من المعلومات، راجع العمل مع إعدادات.

المتطلبات الأساسية

تحتاج إلى المكونات التالية لاستكمال هذه الإرشادات التفصيلية:

إنشاء مشروع WPF

الإجراء الأول هو إنشاء مشروع WPF للتطبيق.

لإنشاء مشروع WPF

استيراد محتوى Direct3D9.

استيراد محتوى Direct3D9 من DLL غير المُدارة باستخدام السمة DllImport.

لاستيراد محتوى Direct3D9

  1. افتح Window1.xaml.cs في محرر تعليمات برمجية.

  2. استبدل التعليمات البرمجية المنشأة تلقائياً بالتعليمات البرمجية التالية.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Interop;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    using System.Windows.Threading;
    using System.Runtime.InteropServices;
    using System.Security.Permissions;
    
    namespace D3DHost
    {
        public partial class Window1 : Window
        {
            public Window1()
            {
                InitializeComponent();
    
                // Set up the initial state for the D3DImage.
                HRESULT.Check(SetSize(512, 512));
                HRESULT.Check(SetAlpha(false));
                HRESULT.Check(SetNumDesiredSamples(4));
    
                // 
                // Optional: Subscribing to the IsFrontBufferAvailableChanged event.
                //
                // If you don't render every frame (e.g. you only render in 
                // reaction to a button click), you should subscribe to the
                // IsFrontBufferAvailableChanged event to be notified when rendered content 
                // is no longer being displayed. This event also notifies you when 
                // the D3DImage is capable of being displayed again. 
    
                // For example, in the button click case, if you don't render again when 
                // the IsFrontBufferAvailable property is set to true, your 
                // D3DImage won't display anything until the next button click.
                //
                // Because this application renders every frame, there is no need to
                // handle the IsFrontBufferAvailableChanged event.
                // 
                CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);
    
                //
                // Optional: Multi-adapter optimization
                //
                // The surface is created initially on a particular adapter.
                // If the WPF window is dragged to another adapter, WPF
                // ensures that the D3DImage still shows up on the new
                // adapter. 
                //
                // This process is slow on Windows XP.
                //
                // Performance is better on Vista with a 9Ex device. It's only 
                // slow when the D3DImage crosses a video-card boundary.
                //
                // To work around this issue, you can move your surface when
                // the D3DImage is displayed on another adapter. To
                // determine when that is the case, transform a point on the
                // D3DImage into screen space and find out which adapter
                // contains that screen space point.
                //
                // When your D3DImage straddles two adapters, nothing  
                // can be done, because one will be updating slowly.
                //
                _adapterTimer = new DispatcherTimer();
                _adapterTimer.Tick += new EventHandler(AdapterTimer_Tick);
                _adapterTimer.Interval = new TimeSpan(0, 0, 0, 0, 500);
                _adapterTimer.Start();
    
                //
                // Optional: Surface resizing
                //
                // The D3DImage is scaled when WPF renders it at a size 
                // different from the natural size of the surface. If the
                // D3DImage is scaled up significantly, image quality 
                // degrades. 
                // 
                // To avoid this, you can either create a very large
                // texture initially, or you can create new surfaces as
                // the size changes. Below is a very simple example of
                // how to do the latter.
                //
                // By creating a timer at Render priority, you are guaranteed
                // that new surfaces are created while the element
                // is still being arranged. A 200 ms interval gives
                // a good balance between image quality and performance.
                // You must be careful not to create new surfaces too 
                // frequently. Frequently allocating a new surface may 
                // fragment or exhaust video memory. This issue is more 
                // significant on XDDM than it is on WDDM, because WDDM 
                // can page out video memory.
                //
                // Another approach is deriving from the Image class, 
                // participating in layout by overriding the ArrangeOverride method, and
                // updating size in the overriden method. Performance will degrade
                // if you resize too frequently.
                //
                // Blurry D3DImages can still occur due to subpixel 
                // alignments. 
                //
                _sizeTimer = new DispatcherTimer(DispatcherPriority.Render);
                _sizeTimer.Tick += new EventHandler(SizeTimer_Tick);
                _sizeTimer.Interval = new TimeSpan(0, 0, 0, 0, 200);
                _sizeTimer.Start();
            }
    
            ~Window1()
            {
                Destroy();
            }
    
            void AdapterTimer_Tick(object sender, EventArgs e)
            {
                POINT p = new POINT(imgelt.PointToScreen(new Point(0, 0)));
    
                HRESULT.Check(SetAdapter(p));
            }
    
            void SizeTimer_Tick(object sender, EventArgs e)
            {
                // The following code does not account for RenderTransforms.
                // To handle that case, you must transform up to the root and 
                // check the size there.
    
                // Given that the D3DImage is at 96.0 DPI, its Width and Height 
                // properties will always be integers. ActualWidth/Height 
                // may not be integers, so they are cast to integers. 
                uint actualWidth = (uint)imgelt.ActualWidth;
                uint actualHeight = (uint)imgelt.ActualHeight;
                if ((actualWidth > 0 && actualHeight > 0) &&
                    (actualWidth != (uint)d3dimg.Width || actualHeight != (uint)d3dimg.Height))
                {
                    HRESULT.Check(SetSize(actualWidth, actualHeight));
                }
            }
    
            void CompositionTarget_Rendering(object sender, EventArgs e)
            {
                RenderingEventArgs args = (RenderingEventArgs)e;
    
                // It's possible for Rendering to call back twice in the same frame 
                // so only render when we haven't already rendered in this frame.
                if (d3dimg.IsFrontBufferAvailable && _lastRender != args.RenderingTime)
                {
                    IntPtr pSurface = IntPtr.Zero;
                    HRESULT.Check(GetBackBufferNoRef(out pSurface));
                    if (pSurface != IntPtr.Zero)
                    {
                        d3dimg.Lock();
                        // Repeatedly calling SetBackBuffer with the same IntPtr is 
                        // a no-op. There is no performance penalty.
                        d3dimg.SetBackBuffer(D3DResourceType.IDirect3DSurface9, pSurface);
                        HRESULT.Check(Render());
                        d3dimg.AddDirtyRect(new Int32Rect(0, 0, d3dimg.PixelWidth, d3dimg.PixelHeight));
                        d3dimg.Unlock();
    
                        _lastRender = args.RenderingTime;
                    }
                }
            }
    
            DispatcherTimer _sizeTimer;
            DispatcherTimer _adapterTimer;
            TimeSpan _lastRender;
    
            // Import the methods exported by the unmanaged Direct3D content.
    
            [DllImport("D3DCode.dll")]
            static extern int GetBackBufferNoRef(out IntPtr pSurface);
    
            [DllImport("D3DCode.dll")]
            static extern int SetSize(uint width, uint height);
    
            [DllImport("D3DCode.dll")]
            static extern int SetAlpha(bool useAlpha);
    
            [DllImport("D3DCode.dll")]
            static extern int SetNumDesiredSamples(uint numSamples);
    
            [StructLayout(LayoutKind.Sequential)]
            struct POINT
            {
                public POINT(Point p)
                {
                    x = (int)p.X;
                    y = (int)p.Y;
                }
    
                public int x;
                public int y;
            }
    
            [DllImport("D3DCode.dll")]
            static extern int SetAdapter(POINT screenSpacePoint);
    
            [DllImport("D3DCode.dll")]
            static extern int Render();
    
            [DllImport("D3DCode.dll")]
            static extern void Destroy();
        }
    
        public static class HRESULT
        {
            [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
            public static void Check(int hr)
            {
                Marshal.ThrowExceptionForHR(hr);
            }
        }
    }
    

استضافة محتوي Direct3D9

وأخيراً، قم باستخدام الفئة D3DImage لاستضافة محتوى Direct3D9.

لاستضافة محتوى Direct3D9

  1. في عرضWindows1.xaml ,قم باستبدال XAML المنشأة تلقائياً مع XAML التالية.

        <Window x:Class="D3DHost.Window1"
        xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="clr-namespace:System.Windows.Interop;assembly=PresentationCore"
        Title="Window1" Height="300" Width="300" Background="PaleGoldenrod">
        <Grid>
            <Image x:Name="imgelt">
                <Image.Source>
                    <i:D3DImage x:Name="d3dimg" />
                </Image.Source>
            </Image>
        </Grid>
    </Window>
    
  2. إنشاء المشروع.

  3. قم بنسخ ملف DLL الذي يحتوي على محتوى Direct3D9 الى المجلد bin/Debug.

  4. اضغط F5 لتشغيل المشروع.

    يظهر محتوى Direct3D9 في تطبيق WPF.

راجع أيضًا:

المرجع

D3DImage

المبادئ

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