#include "pch.h" #include "Direct3DBase.h" 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 device; ComPtr 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(m_renderTargetSize.Width), static_cast(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( m_renderTargetSize.Width); // Match the size of the window. swapChainDesc.Height = static_cast(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 dxgiDevice; DX::ThrowIfFailed(m_d3dDevice.As(&dxgiDevice)); ComPtr dxgiAdapter; DX::ThrowIfFailed(dxgiDevice->GetAdapter(&dxgiAdapter)); ComPtr 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(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 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(m_renderTargetSize.Width), static_cast(m_renderTargetSize.Height), 1, 1, D3D11_BIND_DEPTH_STENCIL); ComPtr 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 }