Direct3DBase.cpp 12 KB

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