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.
Open
Program.cs
and add theusing
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;
Now copy the contents of the
Main()
method from the previous recipe.Build the project (F6) just to be sure everything is setup correctly before continuing.
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
.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>();
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());
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:
Change
null
in the last parameter passed to thenew SwapChain1(...)
constructor toadapter.Outputs[0]
.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.
See also
IDXGIFactory2
documentation on MSDN can be found at http://msdn.microsoft.com/en-us/library/windows/desktop/hh404556(v=vs.85).aspx.Refer Chapter 11, Integrating Direct3D with XAML and Windows 8.1, for more details on creating a device and swap chain for Direct3D 11.2 on Windows 8.1 and Windows Store apps.