Direct3DBase.cpp 12 KB

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