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