Пошаговое руководство. Размещение содержимого Direct3D9 в WPF
Обновлен: Июль 2008
В этом пошаговом руководстве показано, как разместить содержимое Direct3D9 в приложении Windows Presentation Foundation (WPF).
В данном пошаговом руководстве выполняются следующие задачи.
Создание проекта WPF для размещения содержимого Direct3D9.
Импорт содержимого Direct3D9.
Отображение содержимого Direct3D9 с помощью класса D3DImage.
По окончании вы будете знать, как разместить содержимое Direct3D9 в приложении WPF.
![]() |
---|
Отображаемые диалоговые окна и команды меню могут отличаться от описанных в справке, в зависимости от текущих параметров или выпуска. Для изменения параметров выберите пункт Импорт и экспорт параметров в меню Сервис. Дополнительные сведения см. в разделе Параметры Visual Studio. |
Обязательные компоненты
Для выполнения инструкций данного пошагового руководства необходимы указанные ниже компоненты.
Visual Studio 2008 SP1.
DirectX SDK 9 или более поздняя версия.
Файл DLL с содержимым Direct3D9 в формате, совместимом с WPF. Дополнительные сведения см. в разделе Пошаговое руководство. Создание содержимого Direct3D9 для размещения в WPF.
Создание проекта WPF
Первым этапом является создание проекта для приложения WPF.
Создание проекта WPF
Создайте новый проект приложения WPF в Visual C# с именем D3DHost. Дополнительные сведения см. в разделе Практическое руководство. Создание нового проекта приложения WPF.
В WPF (конструктор) откроется файл Window1.xaml.
Импорт содержимого Direct3D9
Импортируйте содержимое Direct3D9 из неуправляемо библиотеки DLL с помощью атрибута DllImport.
Импорт содержимого Direct3D9
В редакторе кода откройте файл Window1.xaml.cs.
Замените автоматически создаваемый код на следующий код.
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
В 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>
Выполните построение проекта.
Скопируйте библиотеку DLL с содержимым Direct3D9 в папку bin/Debug.
Нажмите клавишу F5 для запуска проекта.
Содержимое Direct3D9 будет отображено в приложении WPF.
См. также
Основные понятия
Вопросы производительности, связанные с взаимодействием Direct3D9 и WPF
Ссылки
Журнал изменений
Дата |
Журнал |
Причина |
---|---|---|
Июль 2008 |
Добавление нового раздела. |
Изменение функции SP1. |