src/video/windowsrt/Direct3DBase.cpp
author David Ludwig <dludwig@pobox.com>
Sun, 28 Oct 2012 20:47:33 -0400
changeset 8332 ea83d191cc17
parent 8322 839cd578a474
permissions -rw-r--r--
WinRT: added mouse button event support
dludwig@8322
     1
#include "SDLmain_WinRT_common.h"
dludwig@8322
     2
#include "Direct3DBase.h"
dludwig@8322
     3
dludwig@8322
     4
using namespace DirectX;
dludwig@8322
     5
using namespace Microsoft::WRL;
dludwig@8322
     6
using namespace Windows::UI::Core;
dludwig@8322
     7
using namespace Windows::Foundation;
dludwig@8322
     8
using namespace Windows::Graphics::Display;
dludwig@8322
     9
dludwig@8322
    10
// Constructor.
dludwig@8322
    11
Direct3DBase::Direct3DBase()
dludwig@8322
    12
{
dludwig@8322
    13
}
dludwig@8322
    14
dludwig@8322
    15
// Initialize the Direct3D resources required to run.
dludwig@8322
    16
void Direct3DBase::Initialize(CoreWindow^ window)
dludwig@8322
    17
{
dludwig@8322
    18
	m_window = window;
dludwig@8322
    19
	
dludwig@8322
    20
	CreateDeviceResources();
dludwig@8322
    21
	CreateWindowSizeDependentResources();
dludwig@8322
    22
}
dludwig@8322
    23
dludwig@8322
    24
// Recreate all device resources and set them back to the current state.
dludwig@8322
    25
void Direct3DBase::HandleDeviceLost()
dludwig@8322
    26
{
dludwig@8322
    27
	// Reset these member variables to ensure that UpdateForWindowSizeChange recreates all resources.
dludwig@8322
    28
	m_windowBounds.Width = 0;
dludwig@8322
    29
	m_windowBounds.Height = 0;
dludwig@8322
    30
	m_swapChain = nullptr;
dludwig@8322
    31
dludwig@8322
    32
	CreateDeviceResources();
dludwig@8322
    33
	UpdateForWindowSizeChange();
dludwig@8322
    34
}
dludwig@8322
    35
dludwig@8322
    36
// These are the resources that depend on the device.
dludwig@8322
    37
void Direct3DBase::CreateDeviceResources()
dludwig@8322
    38
{
dludwig@8322
    39
	// This flag adds support for surfaces with a different color channel ordering
dludwig@8322
    40
	// than the API default. It is required for compatibility with Direct2D.
dludwig@8322
    41
	UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
dludwig@8322
    42
dludwig@8322
    43
#if defined(_DEBUG)
dludwig@8322
    44
	// If the project is in a debug build, enable debugging via SDK Layers with this flag.
dludwig@8322
    45
	creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
dludwig@8322
    46
#endif
dludwig@8322
    47
dludwig@8322
    48
	// This array defines the set of DirectX hardware feature levels this app will support.
dludwig@8322
    49
	// Note the ordering should be preserved.
dludwig@8322
    50
	// Don't forget to declare your application's minimum required feature level in its
dludwig@8322
    51
	// description.  All applications are assumed to support 9.1 unless otherwise stated.
dludwig@8322
    52
	D3D_FEATURE_LEVEL featureLevels[] = 
dludwig@8322
    53
	{
dludwig@8322
    54
		D3D_FEATURE_LEVEL_11_1,
dludwig@8322
    55
		D3D_FEATURE_LEVEL_11_0,
dludwig@8322
    56
		D3D_FEATURE_LEVEL_10_1,
dludwig@8322
    57
		D3D_FEATURE_LEVEL_10_0,
dludwig@8322
    58
		D3D_FEATURE_LEVEL_9_3,
dludwig@8322
    59
		D3D_FEATURE_LEVEL_9_2,
dludwig@8322
    60
		D3D_FEATURE_LEVEL_9_1
dludwig@8322
    61
	};
dludwig@8322
    62
dludwig@8322
    63
	// Create the Direct3D 11 API device object and a corresponding context.
dludwig@8322
    64
	ComPtr<ID3D11Device> device;
dludwig@8322
    65
	ComPtr<ID3D11DeviceContext> context;
dludwig@8322
    66
	DX::ThrowIfFailed(
dludwig@8322
    67
		D3D11CreateDevice(
dludwig@8322
    68
			nullptr, // Specify nullptr to use the default adapter.
dludwig@8322
    69
			D3D_DRIVER_TYPE_HARDWARE,
dludwig@8322
    70
			nullptr,
dludwig@8322
    71
			creationFlags, // Set set debug and Direct2D compatibility flags.
dludwig@8322
    72
			featureLevels, // List of feature levels this app can support.
dludwig@8322
    73
			ARRAYSIZE(featureLevels),
dludwig@8322
    74
			D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Windows Store apps.
dludwig@8322
    75
			&device, // Returns the Direct3D device created.
dludwig@8322
    76
			&m_featureLevel, // Returns feature level of device created.
dludwig@8322
    77
			&context // Returns the device immediate context.
dludwig@8322
    78
			)
dludwig@8322
    79
		);
dludwig@8322
    80
dludwig@8322
    81
	// Get the Direct3D 11.1 API device and context interfaces.
dludwig@8322
    82
	DX::ThrowIfFailed(
dludwig@8322
    83
		device.As(&m_d3dDevice)
dludwig@8322
    84
		);
dludwig@8322
    85
dludwig@8322
    86
	DX::ThrowIfFailed(
dludwig@8322
    87
		context.As(&m_d3dContext)
dludwig@8322
    88
		);
dludwig@8322
    89
}
dludwig@8322
    90
dludwig@8322
    91
// Allocate all memory resources that change on a window SizeChanged event.
dludwig@8322
    92
void Direct3DBase::CreateWindowSizeDependentResources()
dludwig@8322
    93
{ 
dludwig@8322
    94
	// Store the window bounds so the next time we get a SizeChanged event we can
dludwig@8322
    95
	// avoid rebuilding everything if the size is identical.
dludwig@8322
    96
	m_windowBounds = m_window->Bounds;
dludwig@8322
    97
dludwig@8322
    98
	// Calculate the necessary swap chain and render target size in pixels.
dludwig@8322
    99
	float windowWidth = ConvertDipsToPixels(m_windowBounds.Width);
dludwig@8322
   100
	float windowHeight = ConvertDipsToPixels(m_windowBounds.Height);
dludwig@8322
   101
dludwig@8322
   102
	// The width and height of the swap chain must be based on the window's
dludwig@8322
   103
	// landscape-oriented width and height. If the window is in a portrait
dludwig@8322
   104
	// orientation, the dimensions must be reversed.
dludwig@8322
   105
	m_orientation = DisplayProperties::CurrentOrientation;
dludwig@8322
   106
	bool swapDimensions =
dludwig@8322
   107
		m_orientation == DisplayOrientations::Portrait ||
dludwig@8322
   108
		m_orientation == DisplayOrientations::PortraitFlipped;
dludwig@8322
   109
	m_renderTargetSize.Width = swapDimensions ? windowHeight : windowWidth;
dludwig@8322
   110
	m_renderTargetSize.Height = swapDimensions ? windowWidth : windowHeight;
dludwig@8322
   111
dludwig@8322
   112
	if(m_swapChain != nullptr)
dludwig@8322
   113
	{
dludwig@8322
   114
		// If the swap chain already exists, resize it.
dludwig@8322
   115
		DX::ThrowIfFailed(
dludwig@8322
   116
			m_swapChain->ResizeBuffers(
dludwig@8322
   117
				2, // Double-buffered swap chain.
dludwig@8322
   118
				static_cast<UINT>(m_renderTargetSize.Width),
dludwig@8322
   119
				static_cast<UINT>(m_renderTargetSize.Height),
dludwig@8322
   120
				DXGI_FORMAT_B8G8R8A8_UNORM,
dludwig@8322
   121
				0
dludwig@8322
   122
				)
dludwig@8322
   123
			);
dludwig@8322
   124
	}
dludwig@8322
   125
	else
dludwig@8322
   126
	{
dludwig@8322
   127
		// Otherwise, create a new one using the same adapter as the existing Direct3D device.
dludwig@8322
   128
		DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0};
dludwig@8322
   129
		swapChainDesc.Width = static_cast<UINT>(m_renderTargetSize.Width); // Match the size of the window.
dludwig@8322
   130
		swapChainDesc.Height = static_cast<UINT>(m_renderTargetSize.Height);
dludwig@8322
   131
		swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // This is the most common swap chain format.
dludwig@8322
   132
		swapChainDesc.Stereo = false;
dludwig@8322
   133
		swapChainDesc.SampleDesc.Count = 1; // Don't use multi-sampling.
dludwig@8322
   134
		swapChainDesc.SampleDesc.Quality = 0;
dludwig@8322
   135
		swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
dludwig@8322
   136
		swapChainDesc.BufferCount = 2; // Use double-buffering to minimize latency.
dludwig@8322
   137
		swapChainDesc.Scaling = DXGI_SCALING_NONE;
dludwig@8322
   138
		swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // All Windows Store apps must use this SwapEffect.
dludwig@8322
   139
		swapChainDesc.Flags = 0;
dludwig@8322
   140
dludwig@8322
   141
		ComPtr<IDXGIDevice1>  dxgiDevice;
dludwig@8322
   142
		DX::ThrowIfFailed(
dludwig@8322
   143
			m_d3dDevice.As(&dxgiDevice)
dludwig@8322
   144
			);
dludwig@8322
   145
dludwig@8322
   146
		ComPtr<IDXGIAdapter> dxgiAdapter;
dludwig@8322
   147
		DX::ThrowIfFailed(
dludwig@8322
   148
			dxgiDevice->GetAdapter(&dxgiAdapter)
dludwig@8322
   149
			);
dludwig@8322
   150
dludwig@8322
   151
		ComPtr<IDXGIFactory2> dxgiFactory;
dludwig@8322
   152
		DX::ThrowIfFailed(
dludwig@8322
   153
			dxgiAdapter->GetParent(
dludwig@8322
   154
				__uuidof(IDXGIFactory2), 
dludwig@8322
   155
				&dxgiFactory
dludwig@8322
   156
				)
dludwig@8322
   157
			);
dludwig@8322
   158
dludwig@8322
   159
		Windows::UI::Core::CoreWindow^ window = m_window.Get();
dludwig@8322
   160
		DX::ThrowIfFailed(
dludwig@8322
   161
			dxgiFactory->CreateSwapChainForCoreWindow(
dludwig@8322
   162
				m_d3dDevice.Get(),
dludwig@8322
   163
				reinterpret_cast<IUnknown*>(window),
dludwig@8322
   164
				&swapChainDesc,
dludwig@8322
   165
				nullptr, // Allow on all displays.
dludwig@8322
   166
				&m_swapChain
dludwig@8322
   167
				)
dludwig@8322
   168
			);
dludwig@8322
   169
			
dludwig@8322
   170
		// Ensure that DXGI does not queue more than one frame at a time. This both reduces latency and
dludwig@8322
   171
		// ensures that the application will only render after each VSync, minimizing power consumption.
dludwig@8322
   172
		DX::ThrowIfFailed(
dludwig@8322
   173
			dxgiDevice->SetMaximumFrameLatency(1)
dludwig@8322
   174
			);
dludwig@8322
   175
	}
dludwig@8322
   176
	
dludwig@8322
   177
	// Set the proper orientation for the swap chain, and generate the
dludwig@8322
   178
	// 3D matrix transformation for rendering to the rotated swap chain.
dludwig@8322
   179
	DXGI_MODE_ROTATION rotation = DXGI_MODE_ROTATION_UNSPECIFIED;
dludwig@8322
   180
	switch (m_orientation)
dludwig@8322
   181
	{
dludwig@8322
   182
		case DisplayOrientations::Landscape:
dludwig@8322
   183
			rotation = DXGI_MODE_ROTATION_IDENTITY;
dludwig@8322
   184
			m_orientationTransform3D = XMFLOAT4X4( // 0-degree Z-rotation
dludwig@8322
   185
				1.0f, 0.0f, 0.0f, 0.0f,
dludwig@8322
   186
				0.0f, 1.0f, 0.0f, 0.0f,
dludwig@8322
   187
				0.0f, 0.0f, 1.0f, 0.0f,
dludwig@8322
   188
				0.0f, 0.0f, 0.0f, 1.0f
dludwig@8322
   189
				);
dludwig@8322
   190
			break;
dludwig@8322
   191
dludwig@8322
   192
		case DisplayOrientations::Portrait:
dludwig@8322
   193
			rotation = DXGI_MODE_ROTATION_ROTATE270;
dludwig@8322
   194
			m_orientationTransform3D = XMFLOAT4X4( // 90-degree Z-rotation
dludwig@8322
   195
				0.0f, 1.0f, 0.0f, 0.0f,
dludwig@8322
   196
				-1.0f, 0.0f, 0.0f, 0.0f,
dludwig@8322
   197
				0.0f, 0.0f, 1.0f, 0.0f,
dludwig@8322
   198
				0.0f, 0.0f, 0.0f, 1.0f
dludwig@8322
   199
				);
dludwig@8322
   200
			break;
dludwig@8322
   201
dludwig@8322
   202
		case DisplayOrientations::LandscapeFlipped:
dludwig@8322
   203
			rotation = DXGI_MODE_ROTATION_ROTATE180;
dludwig@8322
   204
			m_orientationTransform3D = XMFLOAT4X4( // 180-degree Z-rotation
dludwig@8322
   205
				-1.0f, 0.0f, 0.0f, 0.0f,
dludwig@8322
   206
				0.0f, -1.0f, 0.0f, 0.0f,
dludwig@8322
   207
				0.0f, 0.0f, 1.0f, 0.0f,
dludwig@8322
   208
				0.0f, 0.0f, 0.0f, 1.0f
dludwig@8322
   209
				);
dludwig@8322
   210
			break;
dludwig@8322
   211
dludwig@8322
   212
		case DisplayOrientations::PortraitFlipped:
dludwig@8322
   213
			rotation = DXGI_MODE_ROTATION_ROTATE90;
dludwig@8322
   214
			m_orientationTransform3D = XMFLOAT4X4( // 270-degree Z-rotation
dludwig@8322
   215
				0.0f, -1.0f, 0.0f, 0.0f,
dludwig@8322
   216
				1.0f, 0.0f, 0.0f, 0.0f,
dludwig@8322
   217
				0.0f, 0.0f, 1.0f, 0.0f,
dludwig@8322
   218
				0.0f, 0.0f, 0.0f, 1.0f
dludwig@8322
   219
				);
dludwig@8322
   220
			break;
dludwig@8322
   221
dludwig@8322
   222
		default:
dludwig@8322
   223
			throw ref new Platform::FailureException();
dludwig@8322
   224
	}
dludwig@8322
   225
dludwig@8322
   226
	DX::ThrowIfFailed(
dludwig@8322
   227
		m_swapChain->SetRotation(rotation)
dludwig@8322
   228
		);
dludwig@8322
   229
dludwig@8322
   230
	// Create a render target view of the swap chain back buffer.
dludwig@8322
   231
	ComPtr<ID3D11Texture2D> backBuffer;
dludwig@8322
   232
	DX::ThrowIfFailed(
dludwig@8322
   233
		m_swapChain->GetBuffer(
dludwig@8322
   234
			0,
dludwig@8322
   235
			__uuidof(ID3D11Texture2D),
dludwig@8322
   236
			&backBuffer
dludwig@8322
   237
			)
dludwig@8322
   238
		);
dludwig@8322
   239
dludwig@8322
   240
	DX::ThrowIfFailed(
dludwig@8322
   241
		m_d3dDevice->CreateRenderTargetView(
dludwig@8322
   242
			backBuffer.Get(),
dludwig@8322
   243
			nullptr,
dludwig@8322
   244
			&m_renderTargetView
dludwig@8322
   245
			)
dludwig@8322
   246
		);
dludwig@8322
   247
dludwig@8322
   248
	// Create a depth stencil view.
dludwig@8322
   249
	CD3D11_TEXTURE2D_DESC depthStencilDesc(
dludwig@8322
   250
		DXGI_FORMAT_D24_UNORM_S8_UINT, 
dludwig@8322
   251
		static_cast<UINT>(m_renderTargetSize.Width),
dludwig@8322
   252
		static_cast<UINT>(m_renderTargetSize.Height),
dludwig@8322
   253
		1,
dludwig@8322
   254
		1,
dludwig@8322
   255
		D3D11_BIND_DEPTH_STENCIL
dludwig@8322
   256
		);
dludwig@8322
   257
dludwig@8322
   258
	ComPtr<ID3D11Texture2D> depthStencil;
dludwig@8322
   259
	DX::ThrowIfFailed(
dludwig@8322
   260
		m_d3dDevice->CreateTexture2D(
dludwig@8322
   261
			&depthStencilDesc,
dludwig@8322
   262
			nullptr,
dludwig@8322
   263
			&depthStencil
dludwig@8322
   264
			)
dludwig@8322
   265
		);
dludwig@8322
   266
dludwig@8322
   267
	CD3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc(D3D11_DSV_DIMENSION_TEXTURE2D);
dludwig@8322
   268
	DX::ThrowIfFailed(
dludwig@8322
   269
		m_d3dDevice->CreateDepthStencilView(
dludwig@8322
   270
			depthStencil.Get(),
dludwig@8322
   271
			&depthStencilViewDesc,
dludwig@8322
   272
			&m_depthStencilView
dludwig@8322
   273
			)
dludwig@8322
   274
		);
dludwig@8322
   275
dludwig@8322
   276
	// Set the rendering viewport to target the entire window.
dludwig@8322
   277
	CD3D11_VIEWPORT viewport(
dludwig@8322
   278
		0.0f,
dludwig@8322
   279
		0.0f,
dludwig@8322
   280
		m_renderTargetSize.Width,
dludwig@8322
   281
		m_renderTargetSize.Height
dludwig@8322
   282
		);
dludwig@8322
   283
dludwig@8322
   284
	m_d3dContext->RSSetViewports(1, &viewport);
dludwig@8322
   285
}
dludwig@8322
   286
dludwig@8322
   287
// This method is called in the event handler for the SizeChanged event.
dludwig@8322
   288
void Direct3DBase::UpdateForWindowSizeChange()
dludwig@8322
   289
{
dludwig@8322
   290
	if (m_window->Bounds.Width  != m_windowBounds.Width ||
dludwig@8322
   291
		m_window->Bounds.Height != m_windowBounds.Height ||
dludwig@8322
   292
		m_orientation != DisplayProperties::CurrentOrientation)
dludwig@8322
   293
	{
dludwig@8322
   294
		ID3D11RenderTargetView* nullViews[] = {nullptr};
dludwig@8322
   295
		m_d3dContext->OMSetRenderTargets(ARRAYSIZE(nullViews), nullViews, nullptr);
dludwig@8322
   296
		m_renderTargetView = nullptr;
dludwig@8322
   297
		m_depthStencilView = nullptr;
dludwig@8322
   298
		m_d3dContext->Flush();
dludwig@8322
   299
		CreateWindowSizeDependentResources();
dludwig@8322
   300
	}
dludwig@8322
   301
}
dludwig@8322
   302
dludwig@8322
   303
// Method to deliver the final image to the display.
dludwig@8322
   304
void Direct3DBase::Present()
dludwig@8322
   305
{
dludwig@8322
   306
	// The application may optionally specify "dirty" or "scroll"
dludwig@8322
   307
	// rects to improve efficiency in certain scenarios.
dludwig@8322
   308
	DXGI_PRESENT_PARAMETERS parameters = {0};
dludwig@8322
   309
	parameters.DirtyRectsCount = 0;
dludwig@8322
   310
	parameters.pDirtyRects = nullptr;
dludwig@8322
   311
	parameters.pScrollRect = nullptr;
dludwig@8322
   312
	parameters.pScrollOffset = nullptr;
dludwig@8322
   313
	
dludwig@8322
   314
	// The first argument instructs DXGI to block until VSync, putting the application
dludwig@8322
   315
	// to sleep until the next VSync. This ensures we don't waste any cycles rendering
dludwig@8322
   316
	// frames that will never be displayed to the screen.
dludwig@8322
   317
	HRESULT hr = m_swapChain->Present1(1, 0, &parameters);
dludwig@8322
   318
dludwig@8322
   319
	// Discard the contents of the render target.
dludwig@8322
   320
	// This is a valid operation only when the existing contents will be entirely
dludwig@8322
   321
	// overwritten. If dirty or scroll rects are used, this call should be removed.
dludwig@8322
   322
	m_d3dContext->DiscardView(m_renderTargetView.Get());
dludwig@8322
   323
dludwig@8322
   324
	// Discard the contents of the depth stencil.
dludwig@8322
   325
	m_d3dContext->DiscardView(m_depthStencilView.Get());
dludwig@8322
   326
dludwig@8322
   327
	// If the device was removed either by a disconnect or a driver upgrade, we 
dludwig@8322
   328
	// must recreate all device resources.
dludwig@8322
   329
	if (hr == DXGI_ERROR_DEVICE_REMOVED)
dludwig@8322
   330
	{
dludwig@8322
   331
		HandleDeviceLost();
dludwig@8322
   332
	}
dludwig@8322
   333
	else
dludwig@8322
   334
	{
dludwig@8322
   335
		DX::ThrowIfFailed(hr);
dludwig@8322
   336
	}
dludwig@8322
   337
}
dludwig@8322
   338
dludwig@8322
   339
// Method to convert a length in device-independent pixels (DIPs) to a length in physical pixels.
dludwig@8322
   340
float Direct3DBase::ConvertDipsToPixels(float dips)
dludwig@8322
   341
{
dludwig@8322
   342
	static const float dipsPerInch = 96.0f;
dludwig@8322
   343
	return floor(dips * DisplayProperties::LogicalDpi / dipsPerInch + 0.5f); // Round to nearest integer.
dludwig@8322
   344
}