LoginSignup
7

More than 5 years have passed since last update.

UWPでSharpDX

Last updated at Posted at 2016-06-21

そろそろUWPの機が熟したような気がするので手を付けてみる。

VisualStudio2015新規プロジェクトから

  • Visual C# - Windows - Universal - Blank App

を選択。

XamlをやめてCoreWindowに降りる

App.xamlとMainPage.xamlを削除。
新たにApp.csを作成する。

using System;
using System.Diagnostics;
using Windows.ApplicationModel.Core;
using Windows.UI.Core;

namespace App6 // <- プロジェクトにあわせる
{
    class App
    {
        [MTAThread]
        private static void Main()
        {
            var viewFactory = new FrameworkViewSource();
            CoreApplication.Run(viewFactory);
        }

        class FrameworkViewSource : IFrameworkViewSource
        {
            public IFrameworkView CreateView()
            {
                return new FrameworkView();
            }
        }

        class FrameworkView : IFrameworkView
        {
            public void Initialize(CoreApplicationView applicationView)
            {
                Debug.WriteLine("Initialize");
            }

            public void Load(string entryPoint)
            {
                Debug.WriteLine("Load: "+entryPoint);
            }

            public void Run()
            {
                Debug.WriteLine("Run");
            }

            public void SetWindow(CoreWindow window)
            {
                Debug.WriteLine("SetWindow: "+window);
            }

            public void Uninitialize()
            {
                Debug.WriteLine("Uninitialize");
            }
        }
    }
}

これで一応実行できる。UWPもプロジェクト指定のクラス(App6.App)のstatic void Mainから始まっていることがわかった。 Windows.UI.Xaml.Application.Mainも存在しているがprivateで隠蔽されているのかもしれぬ。

SharpDXやってみる

IFrameworkViewこそがUWPで一番ローレベルのCoreWindowにアクセスする手段らしく、
Win32APIがGDIで描画していたように、CoreWindowではIDXGIFactory2::CreateSwapChainForCoreWindow method
によりCoreWindowをバックバッファとするSwapchainを生成することで
D3Dから描画を行う。

IFrameworkView Interface
をC#+SharpDXに書き直してみる。

まず、nugetからSharpDX.Direct3D11とSharpDX.Mathematicsをインストールしておく。3.02だった。

using System;
using System.Diagnostics;
using Windows.ApplicationModel.Activation;
using Windows.ApplicationModel.Core;
using Windows.UI.Core;

namespace App6
{
    class App
    {
        [MTAThread]
        private static void Main()
        {
            var viewFactory = new FrameworkViewSource();
            CoreApplication.Run(viewFactory);
        }

        class FrameworkViewSource : IFrameworkViewSource
        {
            public IFrameworkView CreateView()
            {
                return new FrameworkView();
            }
        }

        class FrameworkView : IFrameworkView
        {
            CoreWindow m_window;
            SharpDX.DXGI.SwapChain1 m_swapChain;
            SharpDX.Direct3D11.Device1 m_d3dDevice;
            SharpDX.Direct3D11.DeviceContext1 m_d3dDeviceContext;
            SharpDX.Direct3D11.RenderTargetView m_renderTargetView;

            public void Initialize(CoreApplicationView applicationView)
            {
                Debug.WriteLine("Initialize");
                applicationView.Activated += OnActivated;
            }

            void OnActivated(
                 CoreApplicationView applicationView,
                 IActivatedEventArgs args
                 )
            {
                // Activate the application window, making it visible and enabling it to receive events.
                CoreWindow.GetForCurrentThread().Activate();
            }

            public void Load(string entryPoint)
            {
                Debug.WriteLine("Load: " + entryPoint);
            }

            public void Run()
            {
                Debug.WriteLine("Run");

                // First, create the Direct3D device.

                // This flag is required in order to enable compatibility with Direct2D.
                var creationFlags = SharpDX.Direct3D11.DeviceCreationFlags.BgraSupport;

#if DEBUG
                // If the project is in a debug build, enable debugging via SDK Layers with this flag.
                creationFlags |= SharpDX.Direct3D11.DeviceCreationFlags.Debug;
#endif

                // This array defines the ordering of feature levels that D3D should attempt to create.
                var featureLevels = new SharpDX.Direct3D.FeatureLevel[]
               {
                    SharpDX.Direct3D.FeatureLevel.Level_11_1,
                    SharpDX.Direct3D.FeatureLevel.Level_11_0,
               };

                using (var d3dDevice = new SharpDX.Direct3D11.Device(SharpDX.Direct3D.DriverType.Hardware
                    , creationFlags, featureLevels))
                {
                    m_d3dDevice = d3dDevice.QueryInterface<SharpDX.Direct3D11.Device1>();
                }
                m_d3dDeviceContext = m_d3dDevice.ImmediateContext1;

                // After the D3D device is created, create additional application resources.
                CreateWindowSizeDependentResources();

                // Enter the render loop.  Note that Windows Store apps should never exit.
                while (true)
                {
                    // Process events incoming to the window.
                    m_window.Dispatcher.ProcessEvents(CoreProcessEventsOption.ProcessAllIfPresent);

                    // Specify the render target we created as the output target.
                    m_d3dDeviceContext.OutputMerger.SetRenderTargets(null,
                        m_renderTargetView
                        );

                    // Clear the render target to a solid color.
                    m_d3dDeviceContext.ClearRenderTargetView(
                        m_renderTargetView,
                        new SharpDX.Color4(0.071f, 0.04f, 0.561f, 1.0f)
                        );

                    // Present the rendered image to the window.  Because the maximum frame latency is set to 1,
                    // the render loop will generally be throttled to the screen refresh rate, typically around
                    // 60Hz, by sleeping the application on Present until the screen is refreshed.
                    m_swapChain.Present(1, 0);
                }
            }

            public void SetWindow(CoreWindow window)
            {
                Debug.WriteLine("SetWindow: " + window);
                m_window = window;

                // Specify the cursor type as the standard arrow cursor.
                m_window.PointerCursor = new CoreCursor(CoreCursorType.Arrow, 0);

                // Allow the application to respond when the window size changes.
                m_window.SizeChanged += OnWindowSizeChanged;
            }

            void OnWindowSizeChanged(
                CoreWindow sender,
                WindowSizeChangedEventArgs args
                )
            {
                m_renderTargetView = null;
                CreateWindowSizeDependentResources();
            }

            public void Uninitialize()
            {
                Debug.WriteLine("Uninitialize");
            }

            // This method creates all application resources that depend on
            // the application window size.  It is called at app initialization,
            // and whenever the application window size changes.
            void CreateWindowSizeDependentResources()
            {
                if (m_swapChain != null)
                {
                    // If the swap chain already exists, resize it.
                    m_swapChain.ResizeBuffers(
                        2,
                        0,
                        0,
                        SharpDX.DXGI.Format.B8G8R8A8_UNorm,
                        0
                    );
                }
                else
                {
                    // If the swap chain does not exist, create it.
                    var swapChainDesc = new SharpDX.DXGI.SwapChainDescription1
                    {
                        Stereo = false,
                        Usage = SharpDX.DXGI.Usage.RenderTargetOutput,
                        Scaling = SharpDX.DXGI.Scaling.None,
                        Flags = 0,
                    };

                    // Use automatic sizing.
                    swapChainDesc.Width = 0;
                    swapChainDesc.Height = 0;

                    // This is the most common swap chain format.
                    swapChainDesc.Format = SharpDX.DXGI.Format.B8G8R8A8_UNorm;

                    // Don't use multi-sampling.
                    swapChainDesc.SampleDescription.Count = 1;
                    swapChainDesc.SampleDescription.Quality = 0;

                    // Use two buffers to enable flip effect.
                    swapChainDesc.BufferCount = 2;

                    // We recommend using this swap effect for all applications.
                    swapChainDesc.SwapEffect = SharpDX.DXGI.SwapEffect.FlipSequential;

                    // Once the swap chain description is configured, it must be
                    // created on the same adapter as the existing D3D Device.

                    // First, retrieve the underlying DXGI Device from the D3D Device.
                    using (var dxgiDevice = m_d3dDevice.QueryInterface<SharpDX.DXGI.Device2>())
                    {

                        // Ensure that DXGI does not queue more than one frame at a time. This both reduces
                        // latency and ensures that the application will only render after each VSync, minimizing
                        // power consumption.
                        dxgiDevice.MaximumFrameLatency = 1;

                        // Next, get the parent factory from the DXGI Device.
                        using (var dxgiAdapter = dxgiDevice.Adapter)
                        using (var dxgiFactory = dxgiAdapter.GetParent<SharpDX.DXGI.Factory2>())
                        // Finally, create the swap chain.
                        using (var coreWindow = new SharpDX.ComObject(m_window))
                        {
                            m_swapChain = new SharpDX.DXGI.SwapChain1(dxgiFactory
                                , m_d3dDevice, coreWindow, ref swapChainDesc);
                        }
                    }
                }

                // Once the swap chain is created, create a render target view.  This will
                // allow Direct3D to render graphics to the window.
                using (var backBuffer = m_swapChain.GetBackBuffer<SharpDX.Direct3D11.Texture2D>(0))
                {
                    m_renderTargetView = new SharpDX.Direct3D11.RenderTargetView(m_d3dDevice, backBuffer);

                    // After the render target view is created, specify that the viewport,
                    // which describes what portion of the window to draw to, should cover
                    // the entire window.

                    var backBufferDesc = backBuffer.Description;

                    var viewport = new SharpDX.ViewportF
                    {
                        X = 0.0f,
                        Y = 0.0f,
                        Width = backBufferDesc.Width,
                        Height = backBufferDesc.Height,
                        MinDepth = 0,
                        MaxDepth = 1,
                    };

                    m_d3dDeviceContext.Rasterizer.SetViewport(viewport);
                }
            }
        }
    }
}

レンダーループで画面をクリアするところまで実装した。CreateWindowのくだりが無い分Win32API版よりだいぶ楽。VisualStudio2015の C++ - Windows - Universal - DirectX12App や DirectX11 and XAML Appもわりと簡単に移植できそうだ。

参考

ちょっと前の本なのでWindows8.1 + D3D11(SharpDX)。UWP + D3D12版が出ないかなー。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7