Finally, it's time to do the hard part: bringing DirectX into the view provider you created for your Windows Store app built for Windows using DirectX with C++. You created the view provider and the factory that creates it, and you prepared the necessary events to handle PLM and basic input. Now, let's add the graphics resources that draw and manage the view, and which update in response to the events received by the app's CoreWindow.
Instructions
In Create and initialize a view, you defined the implementation of the SetWindow method that the app calls when it creates the view provider instance. SetWindow calls the Initialize method on your main app object instance (MyDirectXApp), a method it inherited from its parent class, Direct3DBase. SetWindow calls Initialize to create the main app window and its associated display resources. You aren't required to use DirectXBase in your own implementation; in fact, you could implement the methods below as part of your view provider class. But why not let the Microsoft Visual StudioDirect3D App template do the work for you?
// Initialize the Direct3D resources required to run. void Direct3DBase::Initialize(CoreWindow^ window) { m_window = window; CreateDeviceResources(); CreateWindowSizeDependentResources(); }
As you can see, we've broken down loading the DirectX resources into two methods: CreateWindowSizeDependentResources and CreateDeviceResources.
First, here's the crucial part: setting up the swap chain for your app. Since the swap chain is the buffer that contains the image your app will present for display, it must match the pixel dimensions of the output device (the screen). This means that it has a dependency on the current pixel dimensions of the full screen window for your app, and if that window changes dimensions, the swap chain must be recreated.
Be aware that this method is called not only when you call Direct3DBase::Render for the first time in MyDirectXApp::Run, but also any time the window is resized.
Here we focus on creating the graphics resources that must change when the window size, the window orientation, or output device changes. These resources include the device interface factory used to create the swap chain, and the swap chain itself.
// Allocate all memory resources that change on a window SizeChanged event. void Direct3DBase::CreateWindowSizeDependentResources() { // Store the window bounds so the next time we get a SizeChanged event we can // avoid rebuilding everything if the size is identical. m_windowBounds = m_window->Bounds; // Calculate the necessary swap chain and render target size in pixels. float windowWidth = ConvertDipsToPixels(m_windowBounds.Width); float windowHeight = ConvertDipsToPixels(m_windowBounds.Height); // The width and height of the swap chain must be based on the window's // landscape-oriented width and height. If the window is in a portrait // orientation, the dimensions must be reversed. m_orientation = DisplayProperties::CurrentOrientation; bool swapDimensions = m_orientation == DisplayOrientations::Portrait || m_orientation == DisplayOrientations::PortraitFlipped; m_renderTargetSize.Width = swapDimensions ? windowHeight : windowWidth; m_renderTargetSize.Height = swapDimensions ? windowWidth : windowHeight; if(m_swapChain != nullptr) { // If the swap chain already exists, resize it. DX::ThrowIfFailed( m_swapChain->ResizeBuffers( 2, // Double-buffered swap chain. static_cast<UINT>(m_renderTargetSize.Width), static_cast<UINT>(m_renderTargetSize.Height), DXGI_FORMAT_B8G8R8A8_UNORM, 0 ) ); } else { // Otherwise, create a new one using the same adapter as the existing Direct3D device. DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0}; swapChainDesc.Width = static_cast<UINT>(m_renderTargetSize.Width); // Match the size of the window. swapChainDesc.Height = static_cast<UINT>(m_renderTargetSize.Height); swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // This is the most common swap chain format. swapChainDesc.Stereo = false; swapChainDesc.SampleDesc.Count = 1; // Don't use multi-sampling. swapChainDesc.SampleDesc.Quality = 0; swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapChainDesc.BufferCount = 2; // Use double-buffering to minimize latency. swapChainDesc.Scaling = DXGI_SCALING_NONE; swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // All Windows Store apps must use this SwapEffect. swapChainDesc.Flags = 0; ComPtr<IDXGIDevice1> dxgiDevice; DX::ThrowIfFailed( m_d3dDevice.As(&dxgiDevice) ); ComPtr<IDXGIAdapter> dxgiAdapter; DX::ThrowIfFailed( dxgiDevice->GetAdapter(&dxgiAdapter) ); ComPtr<IDXGIFactory2> dxgiFactory; DX::ThrowIfFailed( dxgiAdapter->GetParent( __uuidof(IDXGIFactory2), &dxgiFactory ) ); Windows::UI::Core::CoreWindow^ window = m_window.Get(); DX::ThrowIfFailed( dxgiFactory->CreateSwapChainForCoreWindow( m_d3dDevice.Get(), reinterpret_cast<IUnknown*>(window), &swapChainDesc, nullptr, // Allow on all displays. &m_swapChain ) ); // Ensure that DXGI doesn't 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. DX::ThrowIfFailed( dxgiDevice->SetMaximumFrameLatency(1) ); } // Set the proper orientation for the swap chain, and generate the // 3D matrix transformation for rendering to the rotated swap chain. DXGI_MODE_ROTATION rotation = DXGI_MODE_ROTATION_UNSPECIFIED; switch (m_orientation) { case DisplayOrientations::Landscape: rotation = DXGI_MODE_ROTATION_IDENTITY; m_orientationTransform3D = XMFLOAT4X4( // 0-degree Z-rotation 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f ); break; case DisplayOrientations::Portrait: rotation = DXGI_MODE_ROTATION_ROTATE270; m_orientationTransform3D = XMFLOAT4X4( // 90-degree Z-rotation 0.0f, 1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f ); break; case DisplayOrientations::LandscapeFlipped: rotation = DXGI_MODE_ROTATION_ROTATE180; m_orientationTransform3D = XMFLOAT4X4( // 180-degree Z-rotation -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f ); break; case DisplayOrientations::PortraitFlipped: rotation = DXGI_MODE_ROTATION_ROTATE90; m_orientationTransform3D = XMFLOAT4X4( // 270-degree Z-rotation 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f ); break; default: throw ref new Platform::FailureException(); } DX::ThrowIfFailed( m_swapChain->SetRotation(rotation) ); // Create a render target view of the swap chain back buffer. ComPtr<ID3D11Texture2D> backBuffer; DX::ThrowIfFailed( m_swapChain->GetBuffer( 0, __uuidof(ID3D11Texture2D), &backBuffer ) ); DX::ThrowIfFailed( m_d3dDevice->CreateRenderTargetView( backBuffer.Get(), nullptr, &m_renderTargetView ) ); // Create a depth stencil view. CD3D11_TEXTURE2D_DESC depthStencilDesc( DXGI_FORMAT_D24_UNORM_S8_UINT, static_cast<UINT>(m_renderTargetSize.Width), static_cast<UINT>(m_renderTargetSize.Height), 1, 1, D3D11_BIND_DEPTH_STENCIL ); ComPtr<ID3D11Texture2D> depthStencil; DX::ThrowIfFailed( m_d3dDevice->CreateTexture2D( &depthStencilDesc, nullptr, &depthStencil ) ); CD3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc(D3D11_DSV_DIMENSION_TEXTURE2D); DX::ThrowIfFailed( m_d3dDevice->CreateDepthStencilView( depthStencil.Get(), &depthStencilViewDesc, &m_depthStencilView ) ); // Set the rendering viewport to target the entire window. CD3D11_VIEWPORT viewport( 0.0f, 0.0f, m_renderTargetSize.Width, m_renderTargetSize.Height ); m_d3dContext->RSSetViewports(1, &viewport); }
First, test if the pointer to the swap chain is not a null reference. If it isn't, it means that the call to this method came after a window resize event, and you must resize the buffers.
If the swap chain pointer is a null reference, then hey! It's time to create a new swap chain, because it's probably the first call to this method from Direct3DBase::Render. After you create the DXGI graphics object factory, call IDXGIFactory2::CreateSwapChainForCoreWindow on it to get the swap chain as a resource for the main window. In this case, a reference to the app's CoreWindow is kept in a global variable (m_window) on the renderer object, and IDXGIFactory2::CreateSwapChainForCoreWindow creates a swap chain using that reference. As a result, any updates to the swap chain, such as calls to IDXGISwapChain::Present, will be associated with your app's CoreWindow.
Now you have a swap chain, either a new one or a resized one. It's time for you to attach all the appropriate resources that will display the results of rendering pipeline (possibly again, in the case of a window resize event). Get the back buffer that you'll use as your Direct3D render target, and the view interface for displaying that render target when you present the swap chain.
This method also provides a few basic features that can improve the quality and performance of the renderer: preset transformation matrices for screen orientation changes, depth stencils, and a default refresh mode for improved power consumption.
You create the resource factory in the next method, CreateDeviceResources. In this method, you create the resources that are bound to a Direct3D device. It's all centralized in this method, in case the resources need to be recreated if a Direct3D device is lost or changed, for example, if the display changes or the video card is removed.
void Direct3DBase::CreateDeviceResources() { // This flag adds support for surfaces with a different color channel ordering // than the API default. It's required for compatibility with Direct2D. UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; #if defined(_DEBUG) // If the project is in a debug build, enable debugging via SDK Layers with this flag. creationFlags |= D3D11_CREATE_DEVICE_DEBUG; #endif // This array defines the set of DirectX hardware feature levels this app will support. // Note the ordering should be preserved. // Don't forget to declare your application's minimum required feature level in its // description. All applications are assumed to support 9.1 unless otherwise stated. D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, D3D_FEATURE_LEVEL_9_1 }; // Create the Direct3D 11 API device object and a corresponding context. ComPtr<ID3D11Device> device; ComPtr<ID3D11DeviceContext> context; DX::ThrowIfFailed( D3D11CreateDevice( nullptr, // Specify nullptr to use the default adapter. D3D_DRIVER_TYPE_HARDWARE, nullptr, creationFlags, // Set set debug and Direct2D compatibility flags. featureLevels, // List of feature levels this app can support. ARRAYSIZE(featureLevels), D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Windows Store apps. &device, // Returns the Direct3D device created. &m_featureLevel, // Returns feature level of device created. &context // Returns the device immediate context. ) ); // Get the Direct3D 11.1 API device and context interfaces. DX::ThrowIfFailed( device.As(&m_d3dDevice) ); DX::ThrowIfFailed( context.As(&m_d3dContext) ); }
Here, you create the Direct3D 11 device — an abstract representation of the graphics hardware interface for use with Direct3D — and obtain a context for it. You also test to see if the device supports Direct 3D 11.1 features, and obtain the DXGI device as well. (The DXGI device represents the graphics hardware interface at its lowest level, and contains information about the graphics adapter itself.)
Be aware that if you're making an app for the Windows Store (of course you are!), you must set the SDKVersion parameter in the D3D11CreateDevice call to D3D11_SDK_VERSION.
So far, you created and configured your Direct3D swap chain. You completed the setup for your graphic resources. Now, you can create your own render method to draw to the Direct3D context for presentation by the swap chain.
void MyDirectXApp::Render() { // Do some rendering to m_d3dContext! }
Finally, you present the swap chain's display buffer with a call to Present.
// Method to deliver the final image to the display. void Direct3DBase::Present() { // The application may optionally specify "dirty" or "scroll" // rects to improve efficiency in certain scenarios. DXGI_PRESENT_PARAMETERS parameters = {0}; parameters.DirtyRectsCount = 0; parameters.pDirtyRects = nullptr; parameters.pScrollRect = nullptr; parameters.pScrollOffset = nullptr; // The first argument instructs DXGI to block until VSync, putting the application // to sleep until the next VSync. This ensures we don't waste any cycles rendering // frames that will never be displayed to the screen. HRESULT hr = m_swapChain->Present1(1, 0, ¶meters); // Discard the contents of the render target. // This is a valid operation only when the existing contents will be entirely // overwritten. If dirty or scroll rects are used, remove this call. m_d3dContext->DiscardView(m_renderTargetView.Get()); // Discard the contents of the depth stencil. m_d3dContext->DiscardView(m_depthStencilView.Get()); // If the device was removed either by a disconnect or a driver upgrade, we // must recreate all device resources. if (hr == DXGI_ERROR_DEVICE_REMOVED) { HandleDeviceLost(); } else { DX::ThrowIfFailed(hr); }
Building your app
If you've used the Direct3D App template, you should see a screen that looks similar to this when you compile and run your project:

If you implemented your own rendering code, hopefully you see the object you defined drawn in lovely full-screen 3-D!
Otherwise, if you haven't implemented any code in the Render method and written to the ID3D11DeviceContext1 (the m_d3dContext field for Direct3DBase in this code), you see an empty screen. That's a bit boring, but hey, it's a great start!
Previous step
Set up the event dispatcherRelated topics
- How to set up your DirectX Windows Store app to display a view
- Complete code for a DirectX Windows Store app
Build date: 3/11/2013