#define NOMINIMAX #include #include #include #include #include #include #include #include #if defined(_DEBUG) # include #endif #include "DeviceResources.h" #include "DirectXHelper.h" using namespace DirectX; using namespace Microsoft::WRL; using namespace Windows::Foundation; using namespace Windows::Graphics::Display; using namespace Windows::UI::Core; using namespace Windows::UI::Xaml::Controls; using namespace Platform; namespace DisplayMetrics { // High resolution displays can require a lot of GPU and battery power to render. // High resolution phones, for example, may suffer from poor battery life if // games attempt to render at 60 frames per second at full fidelity. // The decision to render at full fidelity across all platforms and form factors // should be deliberate. static const bool SupportHighResolutions = false; // The default thresholds that define a "high resolution" display. If the thresholds // are exceeded and SupportHighResolutions is false, the dimensions will be scaled // by 50%. static const float DpiThreshold = 192.0f; // 200% of standard desktop display. static const float WidthThreshold = 1920.0f; // 1080p width. static const float HeightThreshold = 1080.0f; // 1080p height. }; // Constants used to calculate screen rotations. namespace ScreenRotation { // 0-degree Z-rotation static const XMFLOAT4X4 Rotation0( 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 ); // 90-degree Z-rotation static const XMFLOAT4X4 Rotation90( 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 ); // 180-degree Z-rotation static const XMFLOAT4X4 Rotation180( -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 ); // 270-degree Z-rotation static const XMFLOAT4X4 Rotation270( 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 ); }; // Constructor for DeviceResources. DX::DeviceResources::DeviceResources(ID3D11Device *d3d11Device, ID3D12Device *d3d12Device) : m_d3dRenderTargetSize(), m_outputSize(), m_logicalSize(), m_nativeOrientation(DisplayOrientations::None), m_currentOrientation(DisplayOrientations::None), m_dpi(-1.0f), m_deviceRemoved(false), m_d3d11Device(d3d11Device), m_d3d12Device(d3d12Device) { } void DX::DeviceResources::SetSwapChainRotation(IDXGISwapChain3 *swapChain) { // Set the proper orientation for the swap chain, and generate // 3D matrix transformations for rendering to the rotated swap chain. // The 3D matrix is specified explicitly to avoid rounding errors. DXGI_MODE_ROTATION displayRotation = ComputeDisplayRotation(); switch (displayRotation) { case DXGI_MODE_ROTATION_IDENTITY: m_orientationTransform3D = ScreenRotation::Rotation0; break; case DXGI_MODE_ROTATION_ROTATE90: m_orientationTransform3D = ScreenRotation::Rotation270; break; case DXGI_MODE_ROTATION_ROTATE180: m_orientationTransform3D = ScreenRotation::Rotation180; break; case DXGI_MODE_ROTATION_ROTATE270: m_orientationTransform3D = ScreenRotation::Rotation90; break; default: throw ref new FailureException(); } DX::ThrowIfFailed( swapChain->SetRotation(displayRotation) ); } // Determine the dimensions of the render target and whether it will be scaled down. void DX::DeviceResources::UpdateRenderTargetSize() { m_effectiveDpi = m_dpi; // To improve battery life on high resolution devices, render to a smaller render target // and allow the GPU to scale the output when it is presented. if (!DisplayMetrics::SupportHighResolutions && m_dpi > DisplayMetrics::DpiThreshold) { float width = DX::ConvertDipsToPixels(m_logicalSize.Width, m_dpi); float height = DX::ConvertDipsToPixels(m_logicalSize.Height, m_dpi); // When the device is in portrait orientation, height > width. Compare the // larger dimension against the width threshold and the smaller dimension // against the height threshold. if (max(width, height) > DisplayMetrics::WidthThreshold && min(width, height) > DisplayMetrics::HeightThreshold) { // To scale the app we change the effective DPI. Logical size does not change. m_effectiveDpi /= 2.0f; } } // Calculate the necessary render target size in pixels. m_outputSize.Width = DX::ConvertDipsToPixels(m_logicalSize.Width, m_effectiveDpi); m_outputSize.Height = DX::ConvertDipsToPixels(m_logicalSize.Height, m_effectiveDpi); // Prevent zero size DirectX content from being created. m_outputSize.Width = max(m_outputSize.Width, 1); m_outputSize.Height = max(m_outputSize.Height, 1); // The width and height of the swap chain must be based on the window's // natively-oriented width and height. If the window is not in the native // orientation, the dimensions must be reversed. DXGI_MODE_ROTATION displayRotation = ComputeDisplayRotation(); bool swapDimensions = displayRotation == DXGI_MODE_ROTATION_ROTATE90 || displayRotation == DXGI_MODE_ROTATION_ROTATE270; auto fWidth = swapDimensions ? m_outputSize.Height : m_outputSize.Width; auto fHeight = swapDimensions ? m_outputSize.Width : m_outputSize.Height; m_backBufferWidth = lround(fWidth); m_backBufferHeight = lround(fHeight); } // This method is called when the CoreWindow is created (or re-created). void DX::DeviceResources::SetWindow(CoreWindow^ window) { DisplayInformation^ currentDisplayInformation = DisplayInformation::GetForCurrentView(); m_window = window; m_logicalSize = Windows::Foundation::Size(window->Bounds.Width, window->Bounds.Height); m_nativeOrientation = currentDisplayInformation->NativeOrientation; m_currentOrientation = currentDisplayInformation->CurrentOrientation; m_dpi = currentDisplayInformation->LogicalDpi; //CreateWindowSizeDependentResources(); } // This method is called in the event handler for the SizeChanged event. void DX::DeviceResources::SetLogicalSize(Windows::Foundation::Size logicalSize) { if (m_logicalSize != logicalSize) { m_logicalSize = logicalSize; //CreateWindowSizeDependentResources(); } } // This method is called in the event handler for the DpiChanged event. void DX::DeviceResources::SetDpi(float dpi) { if (dpi != m_dpi) { m_dpi = dpi; // When the display DPI changes, the logical size of the window (measured in Dips) also changes and needs to be updated. m_logicalSize = Windows::Foundation::Size(m_window->Bounds.Width, m_window->Bounds.Height); //CreateWindowSizeDependentResources(); } } // This method is called in the event handler for the OrientationChanged event. void DX::DeviceResources::SetCurrentOrientation(DisplayOrientations currentOrientation) { if (m_currentOrientation != currentOrientation) { m_currentOrientation = currentOrientation; //CreateWindowSizeDependentResources(); } } // This method is called in the event handler for the DisplayContentsInvalidated event. void DX::DeviceResources::ValidateDevice() { // The D3D Device is no longer valid if the default adapter changed since the device // was created or if the device has been removed. ComPtr dxgiDevice; if(m_d3d11Device) DX::ThrowIfFailed(m_d3d11Device.As(&dxgiDevice)); else if(m_d3d12Device) DX::ThrowIfFailed(m_d3d12Device.As(&dxgiDevice)); ComPtr deviceAdapter; DX::ThrowIfFailed(dxgiDevice->GetAdapter(&deviceAdapter)); ComPtr deviceFactory; DX::ThrowIfFailed(deviceAdapter->GetParent(IID_PPV_ARGS(&deviceFactory))); // First, get the LUID for the default adapter from when the device was created. DXGI_ADAPTER_DESC previousDesc; { ComPtr previousDefaultAdapter; DX::ThrowIfFailed(deviceFactory->EnumAdapters1(0, &previousDefaultAdapter)); DX::ThrowIfFailed(previousDefaultAdapter->GetDesc(&previousDesc)); } // Next, get the information for the current default adapter. DXGI_ADAPTER_DESC currentDesc; { ComPtr currentDxgiFactory; DX::ThrowIfFailed(CreateDXGIFactory1(IID_PPV_ARGS(¤tDxgiFactory))); ComPtr currentDefaultAdapter; DX::ThrowIfFailed(currentDxgiFactory->EnumAdapters1(0, ¤tDefaultAdapter)); DX::ThrowIfFailed(currentDefaultAdapter->GetDesc(¤tDesc)); } // If the adapter LUIDs don't match, or if the device reports that it has been removed, // a new D3D device must be created. if (previousDesc.AdapterLuid.LowPart != currentDesc.AdapterLuid.LowPart || previousDesc.AdapterLuid.HighPart != currentDesc.AdapterLuid.HighPart || m_d3d11Device && FAILED(m_d3d11Device->GetDeviceRemovedReason()) || m_d3d12Device && FAILED(m_d3d12Device->GetDeviceRemovedReason())) { m_deviceRemoved = true; } } // This method determines the rotation between the display device's native Orientation and the // current display orientation. DXGI_MODE_ROTATION DX::DeviceResources::ComputeDisplayRotation() { DXGI_MODE_ROTATION rotation = DXGI_MODE_ROTATION_UNSPECIFIED; // Note: NativeOrientation can only be Landscape or Portrait even though // the DisplayOrientations enum has other values. switch (m_nativeOrientation) { case DisplayOrientations::Landscape: switch (m_currentOrientation) { case DisplayOrientations::Landscape: rotation = DXGI_MODE_ROTATION_IDENTITY; break; case DisplayOrientations::Portrait: rotation = DXGI_MODE_ROTATION_ROTATE270; break; case DisplayOrientations::LandscapeFlipped: rotation = DXGI_MODE_ROTATION_ROTATE180; break; case DisplayOrientations::PortraitFlipped: rotation = DXGI_MODE_ROTATION_ROTATE90; break; } break; case DisplayOrientations::Portrait: switch (m_currentOrientation) { case DisplayOrientations::Landscape: rotation = DXGI_MODE_ROTATION_ROTATE90; break; case DisplayOrientations::Portrait: rotation = DXGI_MODE_ROTATION_IDENTITY; break; case DisplayOrientations::LandscapeFlipped: rotation = DXGI_MODE_ROTATION_ROTATE270; break; case DisplayOrientations::PortraitFlipped: rotation = DXGI_MODE_ROTATION_ROTATE180; break; } break; } return rotation; }