共用方式為


逐步解說:在 WPF 中裝載 Direct3D9 內容

更新: 2008 年 7 月

本逐步解說示範如何在 Windows Presentation Foundation (WPF) 應用程式中裝載 Direct3D9 內容。

在這個逐步解說中,您會執行下列工作:

  • 建立 WPF 專案裝載 Direct3D9 內容。

  • 匯入 Direct3D9 內容。

  • 使用 D3DImage 類別顯示 Direct3D9 內容。

完成時,您就會了解如何在 WPF 應用程式中裝載 Direct3D9 內容。

注意事項:

您所看到的對話方塊與功能表命令,可能會因您目前使用的設定或版本,而與 [說明] 中描述的不同。若要變更設定,請從 [工具] 功能表中選擇 [匯入和匯出設定]。如需詳細資訊,請參閱 Visual Studio 設定

必要條件

您需要下列元件才能完成此逐步解說:

建立 WPF 專案

第一個步驟是建立 WPF 應用程式的專案。

若要建立 WPF 專案

匯入 Direct3D9 內容

藉由使用 DllImport 屬性,即可以從 Unmanaged DLL 匯入 Direct3D9 內容。

若要匯入 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. 在 Window1.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. 複製包含 Direct3D9 內容的 DLL 到 bin/Debug 資料夾。

  4. 按 F5 執行專案。

    Direct3D9 內容會出現在 WPF 應用程式內。

請參閱

概念

Direct3D9 和 WPF 互通性的效能考量

參考

D3DImage

變更記錄

日期

記錄

原因

2008 年 7 月

新增主題。

SP1 功能變更。