| // clang-format off |
| #include "pch.h" |
| |
| #include "Direct3DBase.h" |
| // clang-format on |
| |
| using namespace DirectX; |
| using namespace Microsoft::WRL; |
| using namespace Windows::UI::Core; |
| using namespace Windows::Foundation; |
| using namespace Windows::Graphics::Display; |
| |
| // Constructor. |
| Direct3DBase::Direct3DBase() |
| { |
| } |
| |
| // Initialize the Direct3D resources required to run. |
| void Direct3DBase::Initialize(CoreWindow ^ window) |
| { |
| m_window = window; |
| |
| CreateDeviceResources(); |
| CreateWindowSizeDependentResources(); |
| } |
| |
| // Recreate all device resources and set them back to the current state. |
| void Direct3DBase::HandleDeviceLost() |
| { |
| // Reset these member variables to ensure that UpdateForWindowSizeChange |
| // recreates all resources. |
| m_windowBounds.Width = 0; |
| m_windowBounds.Height = 0; |
| m_swapChain = nullptr; |
| |
| CreateDeviceResources(); |
| UpdateForWindowSizeChange(); |
| } |
| |
| // These are the resources that depend on the device. |
| void Direct3DBase::CreateDeviceResources() |
| { |
| // This flag adds support for surfaces with a different color channel |
| // ordering |
| // than the API default. It is required for compatibility with Direct2D. |
| UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; |
| |
| #if defined(_DEBUG) |
| // If the project is in a debug build, enable debugging via SDK Layers with |
| // this flag. |
| creationFlags |= D3D11_CREATE_DEVICE_DEBUG; |
| #endif |
| |
| // This array defines the set of DirectX hardware feature levels this app |
| // will support. |
| // Note the ordering should be preserved. |
| // Don't forget to declare your application's minimum required feature level |
| // in its |
| // description. All applications are assumed to support 9.1 unless otherwise |
| // stated. |
| D3D_FEATURE_LEVEL featureLevels[] = { |
| D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, |
| D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, |
| D3D_FEATURE_LEVEL_9_1 |
| }; |
| |
| // Create the Direct3D 11 API device object and a corresponding context. |
| ComPtr<ID3D11Device> device; |
| ComPtr<ID3D11DeviceContext> context; |
| DX::ThrowIfFailed(D3D11CreateDevice( |
| nullptr, // Specify nullptr to use the default adapter. |
| D3D_DRIVER_TYPE_HARDWARE, nullptr, |
| creationFlags, // Set set debug and Direct2D compatibility flags. |
| featureLevels, // List of feature levels this app can support. |
| ARRAYSIZE(featureLevels), |
| D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Windows |
| // Store apps. |
| &device, // Returns the Direct3D device created. |
| &m_featureLevel, // Returns feature level of device created. |
| &context // Returns the device immediate context. |
| )); |
| |
| // Get the Direct3D 11.1 API device and context interfaces. |
| DX::ThrowIfFailed(device.As(&m_d3dDevice)); |
| |
| DX::ThrowIfFailed(context.As(&m_d3dContext)); |
| } |
| |
| // Allocate all memory resources that change on a window SizeChanged event. |
| void Direct3DBase::CreateWindowSizeDependentResources() |
| { |
| // Store the window bounds so the next time we get a SizeChanged event we can |
| // avoid rebuilding everything if the size is identical. |
| m_windowBounds = m_window->Bounds; |
| |
| // Calculate the necessary swap chain and render target size in pixels. |
| float windowWidth = ConvertDipsToPixels(m_windowBounds.Width); |
| float windowHeight = ConvertDipsToPixels(m_windowBounds.Height); |
| |
| // The width and height of the swap chain must be based on the window's |
| // landscape-oriented width and height. If the window is in a portrait |
| // orientation, the dimensions must be reversed. |
| #if WINVER > 0x0602 |
| m_orientation = DisplayInformation::GetForCurrentView()->CurrentOrientation; |
| #else |
| # if PHONE |
| // WP8 doesn't support rotations so always make it landscape |
| m_orientation = DisplayOrientations::Landscape; |
| # else |
| m_orientation = DisplayProperties::CurrentOrientation; |
| # endif |
| #endif |
| bool swapDimensions = m_orientation == DisplayOrientations::Portrait || |
| m_orientation == DisplayOrientations::PortraitFlipped; |
| m_renderTargetSize.Width = swapDimensions ? windowHeight : windowWidth; |
| m_renderTargetSize.Height = swapDimensions ? windowWidth : windowHeight; |
| |
| if (m_swapChain != nullptr) { |
| // If the swap chain already exists, resize it. |
| DX::ThrowIfFailed( |
| m_swapChain->ResizeBuffers(2, // Double-buffered swap chain. |
| static_cast<UINT>(m_renderTargetSize.Width), |
| static_cast<UINT>(m_renderTargetSize.Height), |
| DXGI_FORMAT_B8G8R8A8_UNORM, 0)); |
| } else { |
| // Otherwise, create a new one using the same adapter as the existing |
| // Direct3D device. |
| DXGI_SWAP_CHAIN_DESC1 swapChainDesc = { 0 }; |
| swapChainDesc.Width = static_cast<UINT>( |
| m_renderTargetSize.Width); // Match the size of the window. |
| swapChainDesc.Height = static_cast<UINT>(m_renderTargetSize.Height); |
| swapChainDesc.Format = |
| DXGI_FORMAT_B8G8R8A8_UNORM; // This is the most common swap chain format. |
| swapChainDesc.Stereo = false; |
| swapChainDesc.SampleDesc.Count = 1; // Don't use multi-sampling. |
| swapChainDesc.SampleDesc.Quality = 0; |
| swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; |
| #if PHONE && WINVER <= 0x0602 |
| swapChainDesc.BufferCount = 1; // Use double-buffering to minimize latency. |
| swapChainDesc.Scaling = DXGI_SCALING_STRETCH; // On phone, only stretch and |
| // aspect-ratio stretch |
| // scaling are allowed. |
| swapChainDesc.SwapEffect = |
| DXGI_SWAP_EFFECT_DISCARD; // On phone, no swap effects are supported. |
| #else |
| swapChainDesc.BufferCount = 2; // Use double-buffering to minimize latency. |
| swapChainDesc.Scaling = DXGI_SCALING_NONE; |
| swapChainDesc.SwapEffect = |
| DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // All Windows Store apps must use this |
| // SwapEffect. |
| #endif |
| swapChainDesc.Flags = 0; |
| |
| ComPtr<IDXGIDevice1> dxgiDevice; |
| DX::ThrowIfFailed(m_d3dDevice.As(&dxgiDevice)); |
| |
| ComPtr<IDXGIAdapter> dxgiAdapter; |
| DX::ThrowIfFailed(dxgiDevice->GetAdapter(&dxgiAdapter)); |
| |
| ComPtr<IDXGIFactory2> dxgiFactory; |
| DX::ThrowIfFailed( |
| dxgiAdapter->GetParent(__uuidof(IDXGIFactory2), &dxgiFactory)); |
| |
| Windows::UI::Core::CoreWindow ^ window = m_window.Get(); |
| DX::ThrowIfFailed(dxgiFactory->CreateSwapChainForCoreWindow( |
| m_d3dDevice.Get(), reinterpret_cast<IUnknown*>(window), &swapChainDesc, |
| nullptr, // Allow on all displays. |
| &m_swapChain)); |
| |
| // Ensure that DXGI does not queue more than one frame at a time. This both |
| // reduces latency and |
| // ensures that the application will only render after each VSync, |
| // minimizing power consumption. |
| DX::ThrowIfFailed(dxgiDevice->SetMaximumFrameLatency(1)); |
| } |
| |
| // Set the proper orientation for the swap chain, and generate the |
| // 3D matrix transformation for rendering to the rotated swap chain. |
| DXGI_MODE_ROTATION rotation = DXGI_MODE_ROTATION_UNSPECIFIED; |
| switch (m_orientation) { |
| case DisplayOrientations::Landscape: |
| rotation = DXGI_MODE_ROTATION_IDENTITY; |
| m_orientationTransform3D = XMFLOAT4X4( // 0-degree Z-rotation |
| 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, |
| 0.0f, 0.0f, 0.0f, 1.0f); |
| break; |
| |
| case DisplayOrientations::Portrait: |
| rotation = DXGI_MODE_ROTATION_ROTATE270; |
| m_orientationTransform3D = XMFLOAT4X4( // 90-degree Z-rotation |
| 0.0f, 1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, |
| 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); |
| break; |
| |
| case DisplayOrientations::LandscapeFlipped: |
| rotation = DXGI_MODE_ROTATION_ROTATE180; |
| m_orientationTransform3D = XMFLOAT4X4( // 180-degree Z-rotation |
| -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, |
| 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); |
| break; |
| |
| case DisplayOrientations::PortraitFlipped: |
| rotation = DXGI_MODE_ROTATION_ROTATE90; |
| m_orientationTransform3D = XMFLOAT4X4( // 270-degree Z-rotation |
| 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, |
| 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); |
| break; |
| |
| default: |
| throw ref new Platform::FailureException(); |
| } |
| |
| #if !PHONE || WINVER > 0x0602 |
| DX::ThrowIfFailed(m_swapChain->SetRotation(rotation)); |
| #endif // !PHONE |
| |
| // Create a render target view of the swap chain back buffer. |
| ComPtr<ID3D11Texture2D> backBuffer; |
| DX::ThrowIfFailed( |
| m_swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), &backBuffer)); |
| |
| DX::ThrowIfFailed(m_d3dDevice->CreateRenderTargetView( |
| backBuffer.Get(), nullptr, &m_renderTargetView)); |
| |
| // Create a depth stencil view. |
| CD3D11_TEXTURE2D_DESC depthStencilDesc( |
| DXGI_FORMAT_D24_UNORM_S8_UINT, static_cast<UINT>(m_renderTargetSize.Width), |
| static_cast<UINT>(m_renderTargetSize.Height), 1, 1, |
| D3D11_BIND_DEPTH_STENCIL); |
| |
| ComPtr<ID3D11Texture2D> depthStencil; |
| DX::ThrowIfFailed( |
| m_d3dDevice->CreateTexture2D(&depthStencilDesc, nullptr, &depthStencil)); |
| |
| CD3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc( |
| D3D11_DSV_DIMENSION_TEXTURE2D); |
| DX::ThrowIfFailed(m_d3dDevice->CreateDepthStencilView( |
| depthStencil.Get(), &depthStencilViewDesc, &m_depthStencilView)); |
| |
| // Set the rendering viewport to target the entire window. |
| CD3D11_VIEWPORT viewport(0.0f, 0.0f, m_renderTargetSize.Width, |
| m_renderTargetSize.Height); |
| |
| m_d3dContext->RSSetViewports(1, &viewport); |
| } |
| |
| // This method is called in the event handler for the SizeChanged event. |
| void Direct3DBase::UpdateForWindowSizeChange() |
| { |
| if (m_window->Bounds.Width != m_windowBounds.Width || |
| m_window->Bounds.Height != m_windowBounds.Height || |
| #if WINVER > 0x0602 |
| m_orientation != |
| DisplayInformation::GetForCurrentView()->CurrentOrientation) |
| #else |
| m_orientation != DisplayProperties::CurrentOrientation) |
| #endif |
| { |
| ID3D11RenderTargetView* nullViews[] = { nullptr }; |
| m_d3dContext->OMSetRenderTargets(ARRAYSIZE(nullViews), nullViews, nullptr); |
| m_renderTargetView = nullptr; |
| m_depthStencilView = nullptr; |
| m_d3dContext->Flush(); |
| CreateWindowSizeDependentResources(); |
| } |
| } |
| |
| void Direct3DBase::ReleaseResourcesForSuspending() |
| { |
| // Phone applications operate in a memory-constrained environment, so when |
| // entering |
| // the background it is a good idea to free memory-intensive objects that |
| // will be |
| // easy to restore upon reactivation. The swapchain and backbuffer are good |
| // candidates |
| // here, as they consume a large amount of memory and can be reinitialized |
| // quickly. |
| m_swapChain = nullptr; |
| m_renderTargetView = nullptr; |
| m_depthStencilView = nullptr; |
| } |
| |
| // Method to deliver the final image to the display. |
| void Direct3DBase::Present() |
| { |
| // The first argument instructs DXGI to block until VSync, putting the |
| // application |
| // to sleep until the next VSync. This ensures we don't waste any cycles |
| // rendering |
| // frames that will never be displayed to the screen. |
| #if PHONE && WINVER <= 0x0602 |
| HRESULT hr = m_swapChain->Present(1, 0); |
| #else |
| // The application may optionally specify "dirty" or "scroll" |
| // rects to improve efficiency in certain scenarios. |
| DXGI_PRESENT_PARAMETERS parameters = { 0 }; |
| parameters.DirtyRectsCount = 0; |
| parameters.pDirtyRects = nullptr; |
| parameters.pScrollRect = nullptr; |
| parameters.pScrollOffset = nullptr; |
| |
| HRESULT hr = m_swapChain->Present1(1, 0, ¶meters); |
| #endif |
| |
| // Discard the contents of the render target. |
| // This is a valid operation only when the existing contents will be entirely |
| // overwritten. If dirty or scroll rects are used, this call should be |
| // removed. |
| m_d3dContext->DiscardView(m_renderTargetView.Get()); |
| |
| // Discard the contents of the depth stencil. |
| m_d3dContext->DiscardView(m_depthStencilView.Get()); |
| |
| // If the device was removed either by a disconnect or a driver upgrade, we |
| // must recreate all device resources. |
| if (hr == DXGI_ERROR_DEVICE_REMOVED) { |
| HandleDeviceLost(); |
| } else { |
| DX::ThrowIfFailed(hr); |
| } |
| } |
| |
| // Method to convert a length in device-independent pixels (DIPs) to a length |
| // in physical pixels. |
| float Direct3DBase::ConvertDipsToPixels(float dips) |
| { |
| static const float dipsPerInch = 96.0f; |
| #if WINVER > 0x0602 |
| return floor(dips * DisplayInformation::GetForCurrentView()->LogicalDpi / |
| dipsPerInch + |
| 0.5f); // Round to nearest integer. |
| #else |
| return floor(dips * DisplayProperties::LogicalDpi / dipsPerInch + |
| 0.5f); // Round to nearest integer. |
| #endif |
| } |