Direct3D Rendering Cookbook
上QQ阅读APP看书,第一时间看更新

Initializing a Direct3D 11.1/11.2 device and swap chain

We now know how to create our device and swap chain, however, we do not yet have access to some of the features available in Direct3D 11.1 or 11.2 as we are only creating Direct3D 11 references.

In this recipe we will modify the previous example so that we are instead creating SharpDX.Direct3D11.Device1 and SharpDX.DXGI.SwapChain1 (natively these are ID3D11Device1 and IDXGISwapChain1, respectively) to access Direct3D 11.1 features, and SharpDX.Direct3D11.Device2, and SharpDX.DXGI.SwapChain2 (natively these are ID3D11Device2 and IDXGISwapChain2, respectively) to access the features of Direct3D 11.2.

Device1 allows, among others, the creation of blend states that utilize logical operations and access to DeviceContext1 to access larger constant buffers in shaders than would normally be possible.

SharpDX.DXGI.SwapChain1 includes support for stereoscopic 3D display and supports WinRT and Windows Phone 8 development.

Note

The Direct3D 11.2 API is only available on Windows 8.1.

Getting ready

First we will create a new Windows Form Application project named Ch01_02Direct3D11_1 in our D3DRendering.sln solution.

Now add the SharpDX references as outlined in the previous recipe, choosing the appropriate version – Building a Direct3D 11 application with C# and SharpDX.

Set the new project as the startup project by right-clicking on the project in the solution explorer and click on Set as StartUp Project.

Note

For Windows 7/Windows Server 2008 R2 users, this recipe requires that you have installed the platform update for Windows 7 Service Pack 1/Windows Server 2008 R2 SP1.

It is not possible to use the Direct3D 11.2 API with Windows 7, as this version is available to Windows 8.1 only.

How to do it…

We'll begin by creating the Direct3D 11 device as done in the previous recipe and then query the object for an implementation of the Direct3D 11.1 Device1 COM interface.

  1. Open Program.cs and add the using directives from the previous recipe along with one additional alias:
    using SharpDX;
    using SharpDX.Windows;
    using SharpDX.DXGI;
    using SharpDX.Direct3D11;
    // Resolve name conflicts by explicitly stating the class to use:
    using Device = SharpDX.Direct3D11.Device;
    using Device1 = SharpDX.Direct3D11.Device1;
    
  2. Now copy the contents of the Main() method from the previous recipe.
  3. Build the project (F6) just to be sure everything is setup correctly before continuing.
  4. Within the Main() method, replace the existing device initialization with the following code:
    // Create the device and swapchain
    Device1 device;
    SwapChain1 swapChain;
    
    // First create a regular D3D11 device
    using (var device11 = new Device(
            SharpDX.Direct3D.DriverType.Hardware, 
            DeviceCreationFlags.None,
            new [] {
                SharpDX.Direct3D.FeatureLevel.Level_11_1,
                SharpDX.Direct3D.FeatureLevel.Level_11_0,
            }))
    {
      // Query device for the Device1 interface (ID3D11Device1)
     device = device11.QueryInterfaceOrNull<Device1>();
      if (device == null)
          throw new NotSupportedException( 
              "SharpDX.Direct3D11.Device1 is not supported");
    }

    Note

    We are explicitly excluding feature levels below 11_0 as we will be using SM5 and other Direct3D 11 features.

    Retrieving the Direct3D 11.2 interfaces is performed in the exact same way except with SharpDX.Direct3D11.Device2.

  5. With the device created, we now need to initialize our swap chain as shown in the following code:
    // Rather than create a new DXGI Factory we reuse the
    // one that has been used internally to create the device
    using (var dxgi = device.QueryInterface<SharpDX.DXGI.Device2>())
    using (var adapter = dxgi.Adapter)
    using (var factory = adapter.GetParent<Factory2>())
    {
        var desc1 = new SwapChainDescription1()
        {
            Width = form.ClientSize.Width,
            Height = form.ClientSize.Height,
            Format = Format.R8G8B8A8_UNorm,
     Stereo = false,
            SampleDescription = new SampleDescription(1, 0),
            Usage = Usage.BackBuffer | Usage.RenderTargetOutput,
            BufferCount = 1,
     Scaling = Scaling.Stretch,
            SwapEffect = SwapEffect.Discard,
        };
    
    
        swapChain = new SwapChain1(factory,
            device,
            form.Handle,
            ref desc1,
            new SwapChainFullScreenDescription()
            {
                RefreshRate = new Rational(60, 1),
     Scaling = DisplayModeScaling.Centered,
                Windowed = true
            },
     // Restrict output to specific Output (monitor)
     null);
    }

    Note

    To retrieve the Direct3D 11.2 swap chain, create the swap chain as done here and then use a call to swapChain.QueryInterfaceOrNull<SwapChain2>();

  6. Finally we will change the swapChain.Present call from within the render loop of the previous recipe to:
    // Present the frame
    swapChain.Present(0, PresentFlags.None, new 
     PresentParameters());
    
  7. Run the project (F5). The result should be identical to the previous recipe.

How it works…

Our first change to the previous code is the addition of a new directive using an alias directive for SharpDX.Direct3D11.Device1. We keep the SharpDX.Direct3D11.Device alias because we first create a regular device and then query it for the 11.1 implementation.

Within the Direct3D Initialization region and after the window is created, we have changed the declaration of the device and swapChain variables to be of type Device1 and SwapChain1. We then create Device with the same parameters as before except using a constructor rather than the previous Device.CreateWithSwapChain method. This is done within a using statement so that the reference to the first device is automatically disposed. Within the using block we query the device for a reference to the Device1 class. If the implementation of Device1 was unavailable in the Direct3D API, the return value from device11.QueryInterfaceOrNull<Device1> would be null while using the regular QueryInterface<T> method would result in a SharpDX.SharpDXException being thrown.

Note

All SharpDX classes that wrap a native COM object support a number of variations of the QueryInterface method to query the underlying IUnknown interface.

To create the swap chain, we need to first get a reference to a SharpDX.DXGI.Factory2 instance. Rather than creating a new factory, we will use the one that was initialized internally to create our device. All device instances also implement the interface for SharpDX.DXGI.Device, which gives us access to the Adapter property. As this is provided by the DXGI API we can work our way back from the device to a SharpDX.DXGI.Factory2 instance via the GetParent method.

The equivalent unmanaged example of this section would look something like:

// pd3dDevice creation omitted
IDXGIDevice2* pDXGIDevice;
hr = pd3dDevice->QueryInterface(__uuidof(IDXGIDevice2), 
    &pDXGIDevice);
IDXGIAdapter* pDXGIAdapter;
hr = pDXGIDevice->GetParent(__uuidof(IDXGIAdapter), 
    &pDXGIAdapter);
IDXGIFactory2* pDXGIFactory;
pDXGIAdapter->GetParent(__uuidof(IDXGIFactory2), &pDXGIFactory);

Describing the swap chain for Direct3D 11.1 is slightly different as it separates the description into two structures. The first structure, SwapChainDescription1, describes the buffer size, format, size, usage, and so on like the original but introduces a Stereo and Scaling option and excludes the fullscreen properties. The second structure, SwapChainFullScreenDescription, describes the fullscreen behavior of the swap chain also with a Scaling option.

As this is a desktop application, we use the SwapChain1 constructor that accepts the window handle to create a swap chain for it. We also pass in the swap chain description structures.

Note

For Windows Store apps, we would instead use the appropriate constructor that accepts a Windows.UI.Core.CoreWindow instance. In the case of Windows.UI.Xaml.Controls.SwapChainPanel, no window object is provided and the created swap chain is assigned to the native panel. Details on this are provided in Chapter 11, Integrating Direct3D with XAML and Windows 8.1.

The last parameter of the factory's swap chain creation method allows the application to restrict the display of information to a particular display device. In this case we are not restricting the output, so we are passing null.

Finally we present the back buffer using the recommended Present method override for DXGI 1.2 (IDXGISwapChain1.Present1). The additional PresentParameters parameter allows an application to optimize presentation by specifying scrolling and dirty rectangles, which reduces memory bandwidth and power consumption. In this case we just pass through an empty instance.

There's more…

There are a number of different ways to initialize your Direct3D device and swap chain. For example, if you are enumerating the available adapters and allowing a user to select, which shall be used by the device constructor instead of defaulting to the first, you will already have created a DXGI factory object and the previous code would look a little different.

The output restriction configuration of the swap chain is an interesting concept and easy to demonstrate if you have more than one screen. With the previous example in place:

  1. Change null in the last parameter passed to the new SwapChain1(...) constructor to adapter.Outputs[0].
  2. Change the swap chain present line to:
    swapChain.Present(0, PresentFlags.RestrictToOutput, new PresentParameters());

If you then drag the window so that it sits between your two displays, the result will look something like the following screenshot. Any portion that sits outside of the designated output will not be rendered and appear black.

There's more…

Result of restricting output to the first screen

See also