Step 3: Connect the DirectX swap chain to the UI

Applies to Windows only

To finish setting up DirectX to display a view, it's time to bring DirectX into the view provider you created for your Windows Store app using DirectX with C++. InStep1: Create and initialize a view, you created the view provider and the factory that creates it. Then, in Step 2: Set up the event handlers, you prepared the necessary events to handle PLM, display, and window changes. 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

Before we start this step, let's take a step back and look at the overall approach for handling DirectX resources in the DirectX app template in Microsoft Visual Studio 2013. In the template, all of the DirectX resources are defined on a class called DeviceResources in the DeviceResources.h and DeviceResources.cpp files. A global instance of this class, which contains ComPtr references to all the graphics device resources, is created on the main thread of the app and can be called or accessed through public methods and accessors. Containing all of the DirectX resource setup, configuration, and teardown in this way makes it far more convenient when maintaining and debugging code.


// Controls all the DirectX device resources.
class DeviceResources
{
	public:
		DeviceResources();
		void CreateDeviceIndependentResources();
		void CreateDeviceResources();
		void CreateWindowSizeDependentResources();
		void SetWindow(Windows::UI::Core::CoreWindow^ window);
		void SetDpi(float dpi);
		void UpdateForWindowSizeChange();
		void ValidateDevice();
		void HandleDeviceLost();
		void RegisterDeviceNotify(IDeviceNotify* deviceNotify);
		void Trim();
		void Present();

		// Device Accessors.
		Windows::Foundation::Size GetOutputBounds() const               { return m_outputSize; }

		// D3D Accessors.											  
		ID3D11Device2*			GetD3DDevice() const					{ return m_d3dDevice.Get(); }
		ID3D11DeviceContext2*	GetD3DDeviceContext() const				{ return m_d3dContext.Get(); }
		IDXGISwapChain1*		GetSwapChain() const					{ return m_swapChain.Get(); }
		D3D_FEATURE_LEVEL		GetDeviceFeatureLevel() const			{ return m_d3dFeatureLevel; }
		ID3D11RenderTargetView*	GetBackBufferRenderTargetView() const	{ return m_d3dRenderTargetView.Get(); }
		ID3D11DepthStencilView* GetDepthStencilView() const				{ return m_d3dDepthStencilView.Get(); }
		D3D11_VIEWPORT			GetScreenViewport() const				{ return m_screenViewport; }
		DirectX::XMFLOAT4X4		GetOrientationTransform3D() const		{ return m_orientationTransform3D; }

		// D2D Accessors.											  
		ID2D1Factory2*			GetD2DFactory() const					{ return m_d2dFactory.Get(); }
		ID2D1Device1*			GetD2DDevice() const					{ return m_d2dDevice.Get(); }
		ID2D1DeviceContext1*	GetD2DDeviceContext() const				{ return m_d2dContext.Get(); }
		ID2D1Bitmap1*			GetD2DTargetBitmap() const				{ return m_d2dTargetBitmap.Get(); }
		IDWriteFactory2*		GetDWriteFactory() const				{ return m_dwriteFactory.Get();	 }
		IWICImagingFactory2*	GetWicImagingFactory() const			{ return m_wicFactory.Get(); }
		D2D1::Matrix3x2F		GetOrientationTransform2D() const		{ return m_orientationTransform2D; }

private:
		DXGI_MODE_ROTATION ComputeDisplayRotation();

		// Direct3D objects.
		Microsoft::WRL::ComPtr<ID3D11Device2>			m_d3dDevice;
		Microsoft::WRL::ComPtr<ID3D11DeviceContext2>	m_d3dContext;
		Microsoft::WRL::ComPtr<IDXGISwapChain1>			m_swapChain;

		// Direct3D rendering objects. Required for 3D.
		Microsoft::WRL::ComPtr<ID3D11RenderTargetView>	m_d3dRenderTargetView;
		Microsoft::WRL::ComPtr<ID3D11DepthStencilView>	m_d3dDepthStencilView;
		D3D11_VIEWPORT									m_screenViewport;

		// Direct2D drawing components.
		Microsoft::WRL::ComPtr<ID2D1Factory2>		m_d2dFactory;
		Microsoft::WRL::ComPtr<ID2D1Device1>		m_d2dDevice;
		Microsoft::WRL::ComPtr<ID2D1DeviceContext1>	m_d2dContext;
		Microsoft::WRL::ComPtr<ID2D1Bitmap1>		m_d2dTargetBitmap;

		// DirectWrite drawing components.
		Microsoft::WRL::ComPtr<IDWriteFactory2>		m_dwriteFactory;
		Microsoft::WRL::ComPtr<IWICImagingFactory2>	m_wicFactory;

		// Cached reference to the Window.
		Platform::Agile<Windows::UI::Core::CoreWindow> m_window;

		// Cached device properties.
		D3D_FEATURE_LEVEL								m_d3dFeatureLevel;
		Windows::Foundation::Size						m_d3dRenderTargetSize;
		Windows::Foundation::Size                       m_outputSize;
		Windows::Graphics::Display::DisplayOrientations	m_orientation;
		float											m_dpi;

		// Transforms used for display orientation.
		D2D1::Matrix3x2F	m_orientationTransform2D;
		DirectX::XMFLOAT4X4	m_orientationTransform3D;
};

Here's a quick list of the critical fields on this type:

  • Microsoft::WRL::ComPtr<ID3D11Device2> m_d3dDevice; This is the reference to a virtual representation of the graphics device. You use this instance to acquire and configure individual graphics resources and views, such as constant buffers and memory for 3D graphics elements or rendering output.
  • Microsoft::WRL::ComPtr<ID3D11DeviceContext2> m_d3dContext; This is the reference to the drawing context associated with the 3D device. You use this instance to actually invoke the individual shader programs and draw the results of the graphics pipeline into a render target.
  • Microsoft::WRL::ComPtr<IDXGISwapChain1> m_swapChain; This is the reference to the swap chain that contains the back buffer which receives the rendered results.
  • Platform::Agile<Windows::UI::Core::CoreWindow> m_window; This is the window that hosts the presented swap chain and displays the rendered results from the drawing context.

Basically, you create (and recreate) your graphics resources with ID3D11Device2, as well as create a new instance of IDXGISwapChain2, every time the display device or window is created (or changes). These resources are used by ID3D11DeviceContext2 when each frame is rendered. The output is ultimately written into the back buffer of the swap chain. When the swap chain is presented, the back buffer is drawn to the CoreWindow, and the user sees the results of rendering in the window.

In the template, we've broken down loading the DirectX resources into two methods: CreateWindowSizeDependentResources and CreateDeviceResources, both found in the DeviceResources class implementation.

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 start the app for the first time, but also any time the window is resized or the display changes.

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.


// These resources need to be recreated every time the window size is changed.
void DeviceResources::CreateWindowSizeDependentResources() 
{
	// Clear our previous window size specific context
	ID3D11RenderTargetView* nullViews[] = {nullptr};
	m_d3dContext->OMSetRenderTargets(ARRAYSIZE(nullViews), nullViews, nullptr);
	m_d3dRenderTargetView = nullptr;
	m_d2dContext->SetTarget(nullptr);
	m_d2dTargetBitmap = nullptr;
	m_d3dDepthStencilView = nullptr;
	m_d3dContext->Flush();

	// Store the output bounds so the next time we get a SizeChanged event we can
	// avoid rebuilding everything if the size is identical.
	DisplayInformation^ currentDisplayInformation = DisplayInformation::GetForCurrentView();
	m_outputSize.Width = m_swapChainPanel == nullptr ? m_window->Bounds.Width : static_cast<float>(m_swapChainPanel->ActualWidth);
    m_outputSize.Height = m_swapChainPanel == nullptr ? m_window->Bounds.Height : static_cast<float>(m_swapChainPanel->ActualHeight);

	// Prevent zero size DirectX content from being created
	m_outputSize.Width = m_outputSize.Width > 0 ? m_outputSize.Width : 1;
	m_outputSize.Height = m_outputSize.Height > 0 ? m_outputSize.Height : 1;

	// Calculate the necessary swap chain and render target size in pixels.
	float outputWidthInPixels;
	float outputHeightInPixels;

	if (m_swapChainPanel != nullptr)
	{
		outputWidthInPixels = m_outputSize.Width * m_swapChainPanel->CompositionScaleX;
		outputHeightInPixels = m_outputSize.Height * m_swapChainPanel->CompositionScaleY;
	}
	else
	{
		outputWidthInPixels = DX::ConvertDipsToPixels(m_outputSize.Width, currentDisplayInformation->LogicalDpi);
		outputHeightInPixels = DX::ConvertDipsToPixels(m_outputSize.Height, currentDisplayInformation->LogicalDpi);
	}


	// The width and height of the swap chain must be based on the window's
	// natively-oriented width and height. If the window is not in the native
	// orientation, the dimensions must be reversed.
	m_orientation = currentDisplayInformation->CurrentOrientation;

	DXGI_MODE_ROTATION displayRotation = ComputeDisplayRotation();

	bool swapDimensions = displayRotation == DXGI_MODE_ROTATION_ROTATE90 || displayRotation == DXGI_MODE_ROTATION_ROTATE270;
	m_d3dRenderTargetSize.Width = swapDimensions ? outputHeightInPixels : outputWidthInPixels;
	m_d3dRenderTargetSize.Height = swapDimensions ? outputWidthInPixels : outputHeightInPixels;

	if (m_swapChain)
	{
		// If the swap chain already exists, resize it.
		HRESULT hr = m_swapChain->ResizeBuffers(
			2, // Double-buffered swap chain.
			static_cast<UINT>(m_d3dRenderTargetSize.Width),
			static_cast<UINT>(m_d3dRenderTargetSize.Height),
			DXGI_FORMAT_B8G8R8A8_UNORM,
			0
			);

		if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET)
		{
			// If the device was removed for any reason, a new device and swap chain will need to be created.
			HandleDeviceLost();

			// Everything is set up now. Do not continue execution of this method. 
			return;
		}
		else
		{
			DX::ThrowIfFailed(hr);
		}
	}
	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_d3dRenderTargetSize.Width); // Match the size of the window.
		swapChainDesc.Height = static_cast<UINT>(m_d3dRenderTargetSize.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.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // All Windows Store apps must use this SwapEffect.
		swapChainDesc.Flags = 0;

		// When using XAML interop, change the Scaling to DXGI_SCALING_STRETCH
		if (m_swapChainPanel == nullptr)
		{
			swapChainDesc.Scaling = DXGI_SCALING_NONE;
		}
		else
		{
			swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
		}

		swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;

		// This sequence obtains the DXGI factory that was used to create the Direct3D device above.
		ComPtr<IDXGIDevice3> 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
				)
			);

		DX::ThrowIfFailed(
				dxgiFactory->CreateSwapChainForCoreWindow(
					m_d3dDevice.Get(),
					reinterpret_cast<IUnknown*>(m_window.Get()),
					&swapChainDesc,
					nullptr,
					&m_swapChain
					)
				);;		
		}

		// 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.
		DX::ThrowIfFailed(
			dxgiDevice->SetMaximumFrameLatency(1)
			);
	}

	// Set the proper orientation for the swap chain, and generate 2D and
	// 3D matrix transformations for rendering to the rotated swap chain.
	// Note the rotation angle for the 2D and 3D transforms are different.
	// This is due to the difference in coordinate spaces.  Additionally,
	// the 3D matrix is specified explicitly to avoid rounding errors.

	switch (displayRotation)
	{
	case DXGI_MODE_ROTATION_IDENTITY:
		m_orientationTransform2D = Matrix3x2F::Identity();
		m_orientationTransform3D = ScreenRotation::Rotation0;
		break;

	case DXGI_MODE_ROTATION_ROTATE90:
		m_orientationTransform2D = 
			Matrix3x2F::Rotation(90.0f) *
            Matrix3x2F::Translation(m_outputSize.Height, 0.0f);
        m_orientationTransform3D = ScreenRotation::Rotation270;
		break;

	case DXGI_MODE_ROTATION_ROTATE180:
		m_orientationTransform2D = 
			Matrix3x2F::Rotation(180.0f) *
			Matrix3x2F::Translation(m_outputSize.Width, m_outputSize.Height);
		m_orientationTransform3D = ScreenRotation::Rotation180;
		break;

	case DXGI_MODE_ROTATION_ROTATE270:
		m_orientationTransform2D = 
			Matrix3x2F::Rotation(270.0f) *
            Matrix3x2F::Translation(0.0f, m_outputSize.Width);
        m_orientationTransform3D = ScreenRotation::Rotation90;
		break;

	default:
		throw ref new Platform::FailureException();
	}

	DX::ThrowIfFailed(
		m_swapChain->SetRotation(displayRotation)
		);
  
// Create a render target view of the swap chain back buffer.
	ComPtr<ID3D11Texture2D> backBuffer;
	DX::ThrowIfFailed(
		m_swapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer))
		);

	DX::ThrowIfFailed(
		m_d3dDevice->CreateRenderTargetView(
			backBuffer.Get(),
			nullptr,
			&m_d3dRenderTargetView
			)
		);

	// Create a depth stencil view for use with 3D rendering if needed.
	CD3D11_TEXTURE2D_DESC depthStencilDesc(
		DXGI_FORMAT_D24_UNORM_S8_UINT, 
		static_cast<UINT>(m_d3dRenderTargetSize.Width),
		static_cast<UINT>(m_d3dRenderTargetSize.Height),
		1, // This depth stencil view has only one texture.
		1, // Use a single mipmap level.
		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_d3dDepthStencilView
			)
		);
	
	// Set the 3D rendering viewport to target the entire window.
	m_screenViewport = CD3D11_VIEWPORT(
		0.0f,
		0.0f,
		m_d3dRenderTargetSize.Width,
		m_d3dRenderTargetSize.Height
		);

	m_d3dContext->RSSetViewports(1, &m_screenViewport);

}

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. 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 DeviceResources instance, 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. 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. This typically happens if the display changes or the video card is removed.



// Configures the Direct3D device, and stores handles to it and the device context.
void DeviceResources::CreateDeviceResources() 
{
	// This flag adds support for surfaces with a different color channel ordering
	// than the API default. It is required for compatibility with Direct2D.
	UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;

#if defined(_DEBUG)
	if (DX::SdkLayersAvailable())
	{
		// 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;

	HRESULT hr = D3D11CreateDevice(
		nullptr,					// Specify nullptr to use the default adapter.
		D3D_DRIVER_TYPE_HARDWARE,	// Create a device using the hardware graphics driver.
		0,							// Should be 0 unless the driver is D3D_DRIVER_TYPE_SOFTWARE.
		creationFlags,				// Set debug and Direct2D compatibility flags.
		featureLevels,				// List of feature levels this app can support.
		ARRAYSIZE(featureLevels),	// Size of the list above.
		D3D11_SDK_VERSION,			// Always set this to D3D11_SDK_VERSION for Windows Store apps.
		&device,					// Returns the Direct3D device created.
		&m_d3dFeatureLevel,			// Returns feature level of device created.
		&context					// Returns the device immediate context.
		);

	if (FAILED(hr))
	{
		// If the initialization fails, fall back to the WARP device.
		// For more information on WARP, see: 
		// http://go.microsoft.com/fwlink/p/?LinkID=286690
		DX::ThrowIfFailed(
			D3D11CreateDevice(
				nullptr,
				D3D_DRIVER_TYPE_WARP, // Create a WARP device instead of a hardware device.
				0,
				creationFlags,
				featureLevels,
				ARRAYSIZE(featureLevels),
				D3D11_SDK_VERSION,
				&device,
				&m_d3dFeatureLevel,
				&context
				)
			);
	}

	// Store pointers to the Direct3D 11.1 API device and immediate context.
	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 Direct3D 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.

Now, let's look at how we use these resources. In Create and initialize a view, you defined the implementation of the IFrameworkView::SetWindow method that the app calls when it creates the view provider instance. In the template, this implementation calls a separate SetWindow method defined on the DeviceResources class in DeviceResources.cpp.


void DeviceResources::SetWindow(CoreWindow^ window)
{
	m_window = window;
	
	// SetDpi() will call CreateWindowSizeDependentResources()
	// if those resources have not been created yet.
	SetDpi(DisplayInformation::GetForCurrentView()->LogicalDpi);

	UpdateForWindowSizeChange();
}


void DeviceResources::UpdateForWindowSizeChange()
{
	DisplayInformation^ currentDisplayInformation = DisplayInformation::GetForCurrentView();

	if (m_swapChainPanel == nullptr && (
            m_window->Bounds.Width  != m_outputSize.Width ||
            m_window->Bounds.Height != m_outputSize.Height
        ) || m_orientation != currentDisplayInformation->CurrentOrientation)

	{
		CreateWindowSizeDependentResources();
	}
}

SetWindow, and by association, UpdateForWindowSizeChange, check the current CoreWindow parameters and Windows::Graphics::Display::DisplayInformation data to determine if the graphics resources need to be recreated; specifically, the swap chain and render targets that correspond to the current window size. If a change has happened, his is when you should recreate the resources using the CreateWindowSizeDependentResources you implemented.

CreateDeviceResources, on the other hand, is called when the app launches or when the graphics adapter is lost (or changes). To support that possibility, you should create methods to handle the situation where the user changes the graphics device. The template provides two methods:

  • ValidateDevice, which is called when the DisplayInformation::DisplayContentsInvalidated event is raised. This event occurs when the display requires a redraw due to an internal error or change.
  • HandleDeviceLost, which sets the device to the previous state before the validation error and notifies the app thread that device has been lost and that the resources have been recreated.

// This method is called in the event handler for the DisplayContentsInvalidated event.
void DeviceResources::ValidateDevice()
{
	// The D3D Device is no longer valid if the default adapter changes or if 
	// the device has been removed. 
 
	// First, get the information for the adapter related to the current device. 

	ComPtr<IDXGIDevice3> dxgiDevice;
	DX::ThrowIfFailed(m_d3dDevice.As(&dxgiDevice));

	ComPtr<IDXGIAdapter> deviceAdapter;
	DX::ThrowIfFailed(dxgiDevice->GetAdapter(&deviceAdapter));

	DXGI_ADAPTER_DESC adapterDesc;
	DX::ThrowIfFailed(deviceAdapter->GetDesc(&adapterDesc));

	// Next, get the information for the default adapter. 

	ComPtr<IDXGIFactory2> dxgiFactory;
	DX::ThrowIfFailed(CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory)));

	ComPtr<IDXGIAdapter1> currentAdapter;
	DX::ThrowIfFailed(dxgiFactory->EnumAdapters1(0, &currentAdapter));

	DXGI_ADAPTER_DESC currentDesc;
	DX::ThrowIfFailed(currentAdapter->GetDesc(&currentDesc));

	// If the adapter LUIDs don't match, or if the device reports that it has been removed, 
	// a new D3D device must be created. 

	if (adapterDesc.AdapterLuid.LowPart != currentDesc.AdapterLuid.LowPart ||
		adapterDesc.AdapterLuid.HighPart != currentDesc.AdapterLuid.HighPart ||
		FAILED(m_d3dDevice->GetDeviceRemovedReason()))
	{
		// Release references to resources related to the old device. 
		dxgiDevice = nullptr;
		deviceAdapter = nullptr;

		// Create a new device and swap chain. 
		HandleDeviceLost();
	}
}




// Recreate all device resources and set them back to the current state.
void DeviceResources::HandleDeviceLost()
{
		// Reset these member variables to ensure that SetDpi recreates all resources.
	float dpi = m_dpi;
	m_dpi = -1.0f;
	m_outputSize.Width = 0;
    m_outputSize.Height = 0;
	m_swapChain = nullptr;

	if (m_deviceNotify != nullptr)
	{
		m_deviceNotify->OnDeviceLost();
	}

	CreateDeviceResources();
	SetDpi(dpi);

	if (m_deviceNotify != nullptr)
	{
		m_deviceNotify->OnDeviceRecreated();
	}
}

So far, you created and configured your Direct3D swap chain, and handled events that require recreation of the swap chain. You completed the setup for your graphic resources, and provided methods to handle display changes. 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, &parameters);

	// 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);
	}


Updates for Windows 8.1

Starting in Windows 8.1, all DirectX Windows Store apps must call IDXGIDevice3::Trim when suspending. This call tells the graphics driver to release all temporary buffers allocated for the app, which reduces the chance that the app will be terminated to reclaim memory resources while in the suspend state. This is a certification requirement for Windows 8.1.

Add the code below to your suspend event handler method:

void App::OnSuspending(
    _In_ Platform::Object^ sender,
    _In_ Windows::ApplicationModel::SuspendingEventArgs^ args
    )
{
    Windows::ApplicationModel::SuspendingDeferral^ deferral = args->SuspendingOperation->GetDeferral();

   // Save application data

    m_exampleDxgiAdapter->Trim();
    deferral->Complete();
}

In the Windows 8.1 DirectX templates that ship with Visual Studio 2013, the DXGI adapter is handled by the template's device resources handler object. The templates already include necessary code that notifies the device resources object to call IDXGIDevice3::Trim during suspend.

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:

A cube rendered with direct3d

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 dispatcher

Related topics

How to set up your DirectX Windows Store app to display a view
Complete code for a DirectX Windows Store app

 

 

Show:
© 2014 Microsoft