Direct3DBase.cpp 12 KB

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