手順 3. DirectX スワップ チェーンの UI への接続

Applies to Windows only

いよいよ、ビューを表示するための DirectX の設定は完了です。DirectX と C++ を使った Windows ストア アプリのために作成したビュー プロバイダーに DirectX を組み込みます。「手順 1 : ビューの作成と初期化」では、既にビュー プロバイダーとそれを作成するファクトリを作成しました。「手順 2: イベント ハンドラーのセット アップ」では、PLM、ディスプレイ、およびウィンドウの変更を処理するために必要なイベントを準備しました。ここでは、ビューの描画と管理を行うグラフィックス リソースを追加し、アプリの CoreWindow が受け取ったイベントに応じて更新を行います。

手順

この手順を開始する前に、これまでの手順を振り返り、Microsoft Visual Studio 2013 の DirectX アプリ テンプレートで DirectX リソースを処理する全体的なアプローチを見てみましょう。このテンプレートではすべての DirectX リソースは、DeviceResources.h ファイルと DeviceResources.cpp ファイルの DeviceResources というクラスで定義されています。このクラスのグローバル インスタンスは、すべてのグラフィック デバイス リソースに対する ComPtr 参照を含み、アプリのメイン スレッドで作成され、パブリック メソッドとアクセサーを通じて呼び出すか、アクセスできます。この方法で DirectX リソースのセットアップ、構成、および分解をすべて含むことで、コードの保守とデバッグがはるかに容易になります。


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

ここでは、この型の重要フィールドの簡単な一覧を示します。

  • Microsoft::WRL::ComPtr<ID3D11Device2> m_d3dDevice; これは、グラフィック デバイスの仮想表現に対する参照です。このインスタンスを使って、3D グラフィック要素かレンダリング出力に対する定数バッファーやメモリなど、個々のグラフィック リソースとビューを取得および構成します。
  • Microsoft::WRL::ComPtr<ID3D11DeviceContext2> m_d3dContext; これは、3D デバイスに関連付けられた描画コンテキストへの参照です。このインスタンスを使って、個々のシェーダー プログラムを実際に起動し、レンダー ターゲットにグラフィック パイプラインの結果を描画します。
  • Microsoft::WRL::ComPtr<IDXGISwapChain1> m_swapChain; これは、レンダリングされた結果を受け取るバック バッファーを含むスワップ チェーンへの参照です。
  • Platform::Agile<Windows::UI::Core::CoreWindow> m_window; これは、提示されたスワップ チェーンをホストし、描画コンテキストからのレンダリングされた結果を表示するウィンドウです。

基本的に、ディスプレイ デバイスまたはウィンドウが作成 (または変更) されるたびに、ID3D11Device2 によりグラフィック リソースを作成 (および再作成) すると共に、IDXGISwapChain2 の新しいインスタンスを作成します。これらのリソースは、各フレームがレンダリングされるときに、ID3D11DeviceContext2 によって使用されます。この出力は最終的に、スワップ チェーンのバック バッファーに書き込まれます。このスワップ チェーンが提示されると、バック バッファーが CoreWindow に描画され、ウィンドウでのレンダリングの結果がユーザーに表示されます。

このテンプレートでは、DirectX リソースを読み込む手順が 2 つのメソッド CreateWindowSizeDependentResourcesCreateDeviceResources に分けられています。両メソッドとも、DeviceResources クラス実装に含まれます。

最初は、アプリのスワップ チェーンの設定という重要な手順です。スワップ チェーンはアプリで表示する画像を格納するバッファーであるため、出力デバイス (画面) のピクセル サイズと一致する必要があります。つまり、スワップ チェーンはアプリの全画面ウィンドウの現在のピクセル サイズに依存するため、そのウィンドウのサイズが変わった場合は、スワップ チェーンを再作成する必要があります。

このメソッドは、アプリを最初に開始したときだけでなく、ウィンドウのサイズが変更されたとき、またはディスプレイが変更されたときにも常に呼び出されることに注意してください。

ここでは、ウィンドウのサイズ、ウィンドウの向き、または出力デバイスが変わったときに変更する必要があるグラフィックス リソースを作成する方法について説明します。これらのリソースには、スワップ チェーンの作成に使われるデバイス インターフェイス ファクトリとスワップ チェーン自体が含まれます。


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

}

まず、スワップ チェーンへのポインターが null 参照かどうかを確認します。null 参照でない場合は、このメソッドの呼び出しがウィンドウ サイズ変更イベントの後に発生したことを示します。バッファーのサイズを変更する必要があります。

スワップ チェーンへのポインターが null 参照になっている場合は、新しいスワップ チェーンを作成します。DXGI グラフィックス オブジェクト ファクトリを作成した後、IDXGIFactory2::CreateSwapChainForCoreWindow を呼び出して、メイン ウィンドウへのリソースとしてスワップ チェーンを取得します。この場合は、アプリの CoreWindow への参照が DeviceResources インスタンスでグローバル変数 (m_window) に保持されるため、IDXGIFactory2::CreateSwapChainForCoreWindow ではその参照を使ってスワップ チェーンを作成します。その結果、IDXGISwapChain::Present への呼び出しなど、スワップ チェーンに対するすべての更新がアプリの CoreWindow に関連付けられます。

これで新しいスワップ チェーンまたはサイズ変更されたスワップ チェーンが作成されました。次に、レンダリング パイプラインの結果を表示する、適切なリソースをすべて適用します。Direct3D レンダー ターゲットとして使用するバック バッファーと、スワップ チェーンを表示するときにそのレンダー ターゲットを表示するためのビュー インターフェイスを取得します。

このメソッドは、レンダラーの品質とパフォーマンスを向上できるいくつかの基本的な機能 (画面の向きの変化に対応する事前設定の変換マトリックス、深度ステンシル、消費電力を削減する既定のリフレッシュ モード) を提供します。

次の CreateDeviceResources メソッドのリソース ファクトリを作成します。 このメソッドで、Direct3D デバイスにバインドされたリソースを作成します。Direct3D デバイスがなくなったり変更されたりした場合にリソースを再作成する必要がある場面に備えて、このメソッドにすべて集約されます。これは通常、ディスプレイを変更した場合や、ビデオ カードを取り除いた場合に発生します。



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

}

ここでは、Direct3D 11 デバイス (Direct3D で使用するグラフィックス ハードウェア インターフェイスの抽象的表現) を作成して、そのコンテキストを取得します。また、このデバイスが Direct3D 11.1 機能をサポートしているかどうかを確認し、DXGI デバイスも取得します。(DXGI デバイスは、最も低いレベルのグラフィックス ハードウェア インターフェイスを表し、グラフィック アダプター自体に関する情報を含みます。)

Windows ストア用のアプリを作成する場合は、D3D11CreateDevice 呼び出しの SDKVersion パラメーターを D3D11_SDK_VERSION に設定する必要があります。

次に、こうしたリソースの使用方法について説明します。「ビューの作成と初期化」で、アプリがビュー プロバイダー インスタンスを作成するときに呼び出す IFrameworkView::SetWindow メソッドの実装を定義しました。 テンプレート内のこの実装では、DeviceResources.cpp 内の DeviceResources クラスで定義された別個の SetWindow メソッドを呼び出します。


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、およびそれに関連する UpdateForWindowSizeChange によって、グラフィック リソース、特に現在のウィンドウのサイズに対応するスワップ チェーンおよびレンダー ターゲットを再作成する必要があるかどうかを判別するために、現在の CoreWindow パラメーターおよび Windows::Graphics::Display::DisplayInformation データをチェックします。変更が発生している場合、実装した CreateWindowSizeDependentResources を使ってリソースを再作成する必要があります。

CreateDeviceResourcesはそれに対して、アプリの開始時、またはグラフィック アダプターの喪失 (または変更) 時に呼び出されます。こうした可能性に対応するために、ユーザーがグラフィック デバイスを変更した状況を処理するメソッドを作成する必要があります。このテンプレートには、次の 2 つのメソッドがあります。

  • ValidateDeviceDisplayInformation::DisplayContentsInvalidated イベントの発生時に呼び出されます。このイベントは、内部エラーまたは変更によりディスプレイの再描画が必要な場合に発生します。
  • HandleDeviceLost。検証エラーの前の状態にデバイスを設定したうえで、デバイスが喪失してリソースが再作成されたことをアプリ スレッドに通知します。

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

ここまでで、Direct3D スワップ チェーンを作成、構成し、スワップ チェーンの再作成を必要とするイベントを処理しました。そして、グラフィック リソースのセットアップを完了し、ディスプレイの変更を処理するメソッドを用意しました。これで、スワップ チェーンによって表示される Direct3D コンテキストに描画する独自のレンダラー メソッドを作成できます。


void MyDirectXApp::Render()
{
    // Do some rendering to m_d3dContext!
}

最後に、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);
	}


Windows 8.1 の変更点

Windows 8.1 では、DirectX を使った Windows ストア アプリはいずれも、中断時に IDXGIDevice3::Trim を呼び出す必要があります。この呼び出しは、アプリに割り当てた一時バッファーをすべて解放するようにグラフィックス ドライバーに対して指示するものであり、アプリが中断状態にある間に終了してメモリ リソースが再利用される可能性を低く抑えることができます。Windows 8.1 では、これが認定要件となっています。

中断イベント ハンドラー メソッドに次のコードを追加してください。

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

Visual Studio 2013 に付属する Windows 8.1 の DirectX テンプレートでは、テンプレートのデバイス リソース ハンドラー オブジェクトによって DXGI アダプターが処理されます。このテンプレートには、中断時にデバイス リソース オブジェクトに IDXGIDevice3::Trim を呼び出すように指示するうえで必要なコードがあらかじめ含まれています。

アプリのビルド

Direct3D アプリケーション テンプレートを使った場合は、プロジェクトをコンパイルして実行すると、次のような画面が表示されます。

Direct3D を使ってレンダリングされた立方体

独自のレンダリング コードを実装した場合は、定義したオブジェクトが全画面表示の 3D で表示されます。

また、Render メソッドにコードを実装せず、ID3D11DeviceContext1 (このコードでは Direct3DBasem_d3dContext フィード) に書き込まなかった場合は、空の画面が表示されます。それではあまりおもしろみがありませんが、それでもすばらしい第一歩です。

前のステップ

イベント ディスパッチャーのセットアップ

関連トピック

DirectX Windows ストア アプリでビューを表示するための設定方法
DirectX Windows ストア アプリ フレームワークのコード一式

 

 

表示:
© 2014 Microsoft