| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476 |
- #include "pch.h"
- #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;
- // 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() :
- m_currentFrame(0),
- m_screenViewport(),
- m_rtvDescriptorSize(0),
- m_fenceEvent(0),
- m_d3dRenderTargetSize(),
- m_outputSize(),
- m_logicalSize(),
- m_nativeOrientation(DisplayOrientations::None),
- m_currentOrientation(DisplayOrientations::None),
- m_dpi(-1.0f),
- m_deviceRemoved(false)
- {
- ZeroMemory(m_fenceValues, sizeof(m_fenceValues));
- CreateDeviceIndependentResources();
- CreateDeviceResources();
- }
- // Configures resources that don't depend on the Direct3D device.
- void DX::DeviceResources::CreateDeviceIndependentResources()
- {
- }
- // Configures the Direct3D device, and stores handles to it and the device context.
- void DX::DeviceResources::CreateDeviceResources()
- {
- #if defined(_DEBUG)
- // If the project is in a debug build, enable debugging via SDK Layers.
- {
- ComPtr<ID3D12Debug> debugController;
- if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController))))
- {
- debugController->EnableDebugLayer();
- }
- }
- #endif
- DX::ThrowIfFailed(CreateDXGIFactory1(IID_PPV_ARGS(&m_dxgiFactory)));
- // Create the Direct3D 12 API device object
- HRESULT hr = D3D12CreateDevice(
- nullptr, // Specify nullptr to use the default adapter.
- D3D_FEATURE_LEVEL_11_0, // Minimum feature level this app can support.
- IID_PPV_ARGS(&m_d3dDevice) // Returns the Direct3D device created.
- );
- if (FAILED(hr))
- {
- // If the initialization fails, fall back to the WARP device.
- // For more information on WARP, see:
- // http://go.microsoft.com/fwlink/?LinkId=286690
- ComPtr<IDXGIAdapter> warpAdapter;
- m_dxgiFactory->EnumWarpAdapter(IID_PPV_ARGS(&warpAdapter));
- DX::ThrowIfFailed(
- D3D12CreateDevice(
- warpAdapter.Get(),
- D3D_FEATURE_LEVEL_11_0,
- IID_PPV_ARGS(&m_d3dDevice)
- )
- );
- }
- // Create the command queue.
- D3D12_COMMAND_QUEUE_DESC queueDesc = {};
- queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
- queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
- DX::ThrowIfFailed(m_d3dDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&m_commandQueue)));
- for (UINT n = 0; n < c_frameCount; n++)
- {
- DX::ThrowIfFailed(
- m_d3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&m_commandAllocators[n]))
- );
- }
- // Create synchronization objects.
- DX::ThrowIfFailed(m_d3dDevice->CreateFence(m_fenceValues[m_currentFrame], D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_fence)));
- m_fenceValues[m_currentFrame]++;
- m_fenceEvent = CreateEventEx(nullptr, FALSE, FALSE, EVENT_ALL_ACCESS);
- }
- // These resources need to be recreated every time the window size is changed.
- void DX::DeviceResources::CreateWindowSizeDependentResources()
- {
- // Wait until all previous GPU work is complete.
- WaitForGpu();
- // Clear the previous window size specific content.
- for (UINT n = 0; n < c_frameCount; n++)
- {
- m_renderTargets[n] = nullptr;
- }
- m_rtvHeap = nullptr;
- // Calculate the necessary render target size in pixels.
- m_outputSize.Width = DX::ConvertDipsToPixels(m_logicalSize.Width, m_dpi);
- m_outputSize.Height = DX::ConvertDipsToPixels(m_logicalSize.Height, m_dpi);
- // 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;
- m_d3dRenderTargetSize.Width = swapDimensions ? m_outputSize.Height : m_outputSize.Width;
- m_d3dRenderTargetSize.Height = swapDimensions ? m_outputSize.Width : m_outputSize.Height;
- if (m_swapChain != nullptr)
- {
- // If the swap chain already exists, resize it.
- HRESULT hr = m_swapChain->ResizeBuffers(
- c_frameCount,
- lround(m_d3dRenderTargetSize.Width),
- lround(m_d3dRenderTargetSize.Height),
- DXGI_FORMAT_B8G8R8A8_UNORM,
- 0
- );
- if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET)
- {
- // If the device was removed for any reason, a new device and swap chain will need to be created.
- m_deviceRemoved = true;
- // Do not continue execution of this method. DeviceResources will be destroyed and re-created.
- return;
- }
- else
- {
- DX::ThrowIfFailed(hr);
- }
- }
- else
- {
- // Otherwise, create a new one using the same adapter as the existing Direct3D device.
- DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
- swapChainDesc.Width = lround(m_d3dRenderTargetSize.Width); // Match the size of the window.
- swapChainDesc.Height = lround(m_d3dRenderTargetSize.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;
- swapChainDesc.BufferCount = c_frameCount; // Use triple-buffering to minimize latency.
- swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; // All Windows Universal apps must use _FLIP_ SwapEffects
- swapChainDesc.Flags = 0;
- swapChainDesc.Scaling = DXGI_SCALING_NONE;
- swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
- ComPtr<IDXGISwapChain1> swapChain;
- DX::ThrowIfFailed(
- m_dxgiFactory->CreateSwapChainForCoreWindow(
- m_commandQueue.Get(),
- reinterpret_cast<IUnknown*>(m_window.Get()),
- &swapChainDesc,
- nullptr,
- &swapChain
- )
- );
- DX::ThrowIfFailed(swapChain.As(&m_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.
- 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(
- m_swapChain->SetRotation(displayRotation)
- );
- // Create a render target view of the swap chain back buffer.
- {
- D3D12_DESCRIPTOR_HEAP_DESC desc = {};
- desc.NumDescriptors = c_frameCount;
- desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
- desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
- DX::ThrowIfFailed(m_d3dDevice->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&m_rtvHeap)));
- m_rtvHeap->SetName(L"Render Target View Descriptor Heap");
- // All pending GPU work was already finished. Update the tracked fence values
- // to the last value signaled.
- for (UINT n = 0; n < c_frameCount; n++)
- {
- m_fenceValues[n] = m_fenceValues[m_currentFrame];
- }
- m_currentFrame = 0;
- CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor(m_rtvHeap->GetCPUDescriptorHandleForHeapStart());
- m_rtvDescriptorSize = m_d3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
- for (UINT n = 0; n < c_frameCount; n++)
- {
- DX::ThrowIfFailed(m_swapChain->GetBuffer(n, IID_PPV_ARGS(&m_renderTargets[n])));
- m_d3dDevice->CreateRenderTargetView(m_renderTargets[n].Get(), nullptr, rtvDescriptor);
- rtvDescriptor.Offset(m_rtvDescriptorSize);
- WCHAR name[25];
- swprintf_s(name, L"Render Target %d", n);
- m_renderTargets[n]->SetName(name);
- }
- }
- // Set the 3D rendering viewport to target the entire window.
- m_screenViewport = { 0.0f, 0.0f, m_d3dRenderTargetSize.Width, m_d3dRenderTargetSize.Height, 0.0f, 1.0f };
- }
- // 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.
- // First, get the LUID for the adapter from when the device was created.
- LUID previousAdapterLuid = m_d3dDevice->GetAdapterLuid();
- // Next, get the information for the current default adapter.
- ComPtr<IDXGIFactory2> currentFactory;
- DX::ThrowIfFailed(CreateDXGIFactory1(IID_PPV_ARGS(¤tFactory)));
- ComPtr<IDXGIAdapter1> currentDefaultAdapter;
- DX::ThrowIfFailed(currentFactory->EnumAdapters1(0, ¤tDefaultAdapter));
- DXGI_ADAPTER_DESC currentDesc;
- 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 (previousAdapterLuid.LowPart != currentDesc.AdapterLuid.LowPart ||
- previousAdapterLuid.HighPart != currentDesc.AdapterLuid.HighPart ||
- FAILED(m_d3dDevice->GetDeviceRemovedReason()))
- {
- m_deviceRemoved = true;
- }
- }
- // Present the contents of the swap chain to the screen.
- void DX::DeviceResources::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.
- HRESULT hr = m_swapChain->Present(1, 0);
- // If the device was removed either by a disconnection or a driver upgrade, we
- // must recreate all device resources.
- if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET)
- {
- m_deviceRemoved = true;
- }
- else
- {
- DX::ThrowIfFailed(hr);
- MoveToNextFrame();
- }
- }
- // Wait for pending GPU work to complete.
- void DX::DeviceResources::WaitForGpu()
- {
- // Schedule a Signal command in the queue.
- DX::ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), m_fenceValues[m_currentFrame]));
- // Wait until the fence has been crossed.
- DX::ThrowIfFailed(m_fence->SetEventOnCompletion(m_fenceValues[m_currentFrame], m_fenceEvent));
- WaitForSingleObjectEx(m_fenceEvent, INFINITE, FALSE);
- // Increment the fence value for the current frame.
- m_fenceValues[m_currentFrame]++;
- }
- // Prepare to render the next frame.
- void DX::DeviceResources::MoveToNextFrame()
- {
- // Schedule a Signal command in the queue.
- const UINT64 currentFenceValue = m_fenceValues[m_currentFrame];
- DX::ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), currentFenceValue));
- // Advance the frame index.
- m_currentFrame = (m_currentFrame + 1) % c_frameCount;
- // Check to see if the next frame is ready to start.
- if (m_fence->GetCompletedValue() < m_fenceValues[m_currentFrame])
- {
- DX::ThrowIfFailed(m_fence->SetEventOnCompletion(m_fenceValues[m_currentFrame], m_fenceEvent));
- WaitForSingleObjectEx(m_fenceEvent, INFINITE, FALSE);
- }
- // Set the fence value for the next frame.
- m_fenceValues[m_currentFrame] = currentFenceValue + 1;
- }
- // 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;
- }
|