1 //// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
2 //// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
3 //// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
4 //// PARTICULAR PURPOSE.
6 //// Copyright (c) Microsoft Corporation. All rights reserved
9 #include "DirectXBase.h"
11 using namespace Windows::UI::Core;
12 using namespace Windows::Foundation;
13 using namespace Microsoft::WRL;
14 using namespace Windows::Graphics::Display;
18 DirectXBase::DirectXBase() :
24 // Initialize the Direct3D resources required to run.
25 void DirectXBase::Initialize(CoreWindow^ window, float dpi)
29 CreateDeviceIndependentResources();
30 CreateDeviceResources();
34 // These are the resources required independent of hardware.
35 void DirectXBase::CreateDeviceIndependentResources()
37 D2D1_FACTORY_OPTIONS options;
38 ZeroMemory(&options, sizeof(D2D1_FACTORY_OPTIONS));
41 // If the project is in a debug build, enable Direct2D debugging via SDK Layers
42 options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
47 D2D1_FACTORY_TYPE_SINGLE_THREADED,
48 __uuidof(ID2D1Factory1),
56 DWRITE_FACTORY_TYPE_SHARED,
57 __uuidof(IDWriteFactory),
64 CLSID_WICImagingFactory,
67 IID_PPV_ARGS(&m_wicFactory)
72 // These are the resources that depend on the device.
73 void DirectXBase::CreateDeviceResources()
75 // This flag adds support for surfaces with a different color channel ordering than the API default.
76 // It is recommended usage, and is required for compatibility with Direct2D.
77 UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
78 ComPtr<IDXGIDevice> dxgiDevice;
81 // If the project is in a debug build, enable debugging via SDK Layers with this flag.
82 creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
85 // This array defines the set of DirectX hardware feature levels this app will support.
86 // Note the ordering should be preserved.
87 // Don't forget to declare your application's minimum required feature level in its
88 // description. All applications are assumed to support 9.1 unless otherwise stated.
89 D3D_FEATURE_LEVEL featureLevels[] =
91 D3D_FEATURE_LEVEL_11_1,
92 D3D_FEATURE_LEVEL_11_0,
93 D3D_FEATURE_LEVEL_10_1,
94 D3D_FEATURE_LEVEL_10_0,
95 D3D_FEATURE_LEVEL_9_3,
96 D3D_FEATURE_LEVEL_9_2,
100 // Create the DX11 API device object, and get a corresponding context.
101 ComPtr<ID3D11Device> device;
102 ComPtr<ID3D11DeviceContext> context;
105 nullptr, // specify null to use the default adapter
106 D3D_DRIVER_TYPE_HARDWARE,
107 0, // leave as 0 unless software device
108 creationFlags, // optionally set debug and Direct2D compatibility flags
109 featureLevels, // list of feature levels this app can support
110 ARRAYSIZE(featureLevels), // number of entries in above list
111 D3D11_SDK_VERSION, // always set this to D3D11_SDK_VERSION for Metro style apps
112 &device, // returns the Direct3D device created
113 &m_featureLevel, // returns feature level of device created
114 &context // returns the device immediate context
118 // Get the DirectX11.1 device by QI off the DirectX11 one.
120 device.As(&m_d3dDevice)
123 // And get the corresponding device context in the same way.
125 context.As(&m_d3dContext)
128 // Obtain the underlying DXGI device of the Direct3D11.1 device.
130 m_d3dDevice.As(&dxgiDevice)
133 // Obtain the Direct2D device for 2-D rendering.
135 m_d2dFactory->CreateDevice(dxgiDevice.Get(), &m_d2dDevice)
138 // And get its corresponding device context object.
140 m_d2dDevice->CreateDeviceContext(
141 D2D1_DEVICE_CONTEXT_OPTIONS_NONE,
146 // Release the swap chain (if it exists) as it will be incompatible with
148 m_swapChain = nullptr;
151 // Helps track the DPI in the helper class.
152 // This is called in the dpiChanged event handler in the view class.
153 void DirectXBase::SetDpi(float dpi)
157 // Save the DPI of this display in our class.
160 // Update Direct2D's stored DPI.
161 m_d2dContext->SetDpi(m_dpi, m_dpi);
163 // Often a DPI change implies a window size change. In some cases Windows will issue
164 // both a size changed event and a DPI changed event. In this case, the resulting bounds
165 // will not change, and the window resize code will only be executed once.
166 UpdateForWindowSizeChange();
170 // This routine is called in the event handler for the view SizeChanged event.
171 void DirectXBase::UpdateForWindowSizeChange()
173 // Only handle window size changed if there is no pending DPI change.
174 if (m_dpi != DisplayProperties::LogicalDpi)
179 if (m_window->Bounds.Width != m_windowBounds.Width ||
180 m_window->Bounds.Height != m_windowBounds.Height)
182 m_d2dContext->SetTarget(nullptr);
183 m_d2dTargetBitmap = nullptr;
184 m_renderTargetView = nullptr;
185 m_depthStencilView = nullptr;
186 CreateWindowSizeDependentResources();
190 // Allocate all memory resources that change on a window SizeChanged event.
191 void DirectXBase::CreateWindowSizeDependentResources()
193 // Store the window bounds so the next time we get a SizeChanged event we can
194 // avoid rebuilding everything if the size is identical.
195 m_windowBounds = m_window->Bounds;
197 // If the swap chain already exists, resize it.
198 if (m_swapChain != nullptr)
201 m_swapChain->ResizeBuffers(m_numBuffers, 0, 0, DXGI_FORMAT_B8G8R8A8_UNORM, 0)
204 else // Otherwise, create a new one.
206 // Allocate a descriptor.
207 DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0};
208 swapChainDesc.Width = 0; // use automatic sizing
209 swapChainDesc.Height = 0;
210 swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // this is the most common swapchain format
211 swapChainDesc.Stereo = false;
212 swapChainDesc.SampleDesc.Count = 1; // don't use multi-sampling
213 swapChainDesc.SampleDesc.Quality = 0;
214 swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
215 swapChainDesc.BufferCount = m_numBuffers; // use multiple buffering to enable flip
216 swapChainDesc.Scaling = DXGI_SCALING_NONE;
217 swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // all Metro style apps must use this SwapEffect
218 swapChainDesc.Flags = 0;
220 // Once the desired swap chain description is configured, it must be created on the same adapter as our D3D Device
222 // First, retrieve the underlying DXGI Device from the D3D Device.
223 ComPtr<IDXGIDevice1> dxgiDevice;
225 m_d3dDevice.As(&dxgiDevice)
228 // Identify the physical adapter (GPU or card) this device is running on.
229 ComPtr<IDXGIAdapter> dxgiAdapter;
231 dxgiDevice->GetAdapter(&dxgiAdapter)
234 // And obtain the factory object that created it.
235 ComPtr<IDXGIFactory2> dxgiFactory;
237 dxgiAdapter->GetParent(IID_PPV_ARGS(&dxgiFactory))
240 // Obtain the final swap chain for this window from the DXGI factory.
241 CoreWindow^ window = m_window.Get();
243 dxgiFactory->CreateSwapChainForCoreWindow(
245 reinterpret_cast<IUnknown*>(window),
247 nullptr, // allow on all displays
252 // Ensure that DXGI does not queue too many frames. This reduces latency and
253 // ensures that the application will only render after each VSync, minimizing
254 // power consumption.
256 dxgiDevice->SetMaximumFrameLatency(m_numBuffers - 1)
260 // Obtain the backbuffer for this window which will be the final 3D rendertarget.
261 ComPtr<ID3D11Texture2D> backBuffer;
263 m_swapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer))
266 // Create a view interface on the rendertarget to use on bind.
268 m_d3dDevice->CreateRenderTargetView(
275 // Cache the rendertarget dimensions in our helper class for convenient use.
276 D3D11_TEXTURE2D_DESC backBufferDesc = {0};
277 backBuffer->GetDesc(&backBufferDesc);
278 m_renderTargetSize.Width = static_cast<float>(backBufferDesc.Width);
279 m_renderTargetSize.Height = static_cast<float>(backBufferDesc.Height);
281 // Create a descriptor for the depth/stencil buffer.
282 CD3D11_TEXTURE2D_DESC depthStencilDesc(
283 DXGI_FORMAT_D24_UNORM_S8_UINT,
284 backBufferDesc.Width,
285 backBufferDesc.Height,
288 D3D11_BIND_DEPTH_STENCIL
291 // Allocate a 2-D surface as the depth/stencil buffer.
292 ComPtr<ID3D11Texture2D> depthStencil;
294 m_d3dDevice->CreateTexture2D(
301 // Create a DepthStencil view on this surface to use on bind.
302 auto viewDesc = CD3D11_DEPTH_STENCIL_VIEW_DESC(D3D11_DSV_DIMENSION_TEXTURE2D);
304 m_d3dDevice->CreateDepthStencilView(
311 // Create a viewport descriptor of the full window size.
312 CD3D11_VIEWPORT viewport(
315 static_cast<float>(backBufferDesc.Width),
316 static_cast<float>(backBufferDesc.Height)
319 // Set the current viewport using the descriptor.
320 m_d3dContext->RSSetViewports(1, &viewport);
322 // Now we set up the Direct2D render target bitmap linked to the swapchain.
323 // Whenever we render to this bitmap, it will be directly rendered to the
324 // swapchain associated with the window.
325 D2D1_BITMAP_PROPERTIES1 bitmapProperties =
327 D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
328 PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
333 // Direct2D needs the dxgi version of the backbuffer surface pointer.
334 ComPtr<IDXGISurface> dxgiBackBuffer;
336 m_swapChain->GetBuffer(0, IID_PPV_ARGS(&dxgiBackBuffer))
339 // Get a D2D surface from the DXGI back buffer to use as the D2D render target.
341 m_d2dContext->CreateBitmapFromDxgiSurface(
342 dxgiBackBuffer.Get(),
348 // So now we can set the Direct2D render target.
349 m_d2dContext->SetTarget(m_d2dTargetBitmap.Get());
351 // Set D2D text anti-alias mode to Grayscale to ensure proper rendering of text on intermediate surfaces.
352 m_d2dContext->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE);
355 // Method to deliver the final image to the display.
356 void DirectXBase::Present()
358 // The application may optionally specify "dirty" or "scroll" rects to improve efficiency
359 // in certain scenarios. In this sample, however, we do not utilize those features.
360 DXGI_PRESENT_PARAMETERS parameters = {0};
361 parameters.DirtyRectsCount = 0;
362 parameters.pDirtyRects = nullptr;
363 parameters.pScrollRect = nullptr;
364 parameters.pScrollOffset = nullptr;
366 // The first argument instructs DXGI to block until VSync, putting the application
367 // to sleep until the next VSync. This ensures we don't waste any cycles rendering
368 // frames that will never be displayed to the screen.
369 HRESULT hr = m_swapChain->Present1(1, 0, ¶meters);
371 // If the device was removed either by a disconnect or a driver upgrade, we
372 // must completely reinitialize the renderer.
373 if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET)
375 Initialize(m_window.Get(), m_dpi);
379 DX::ThrowIfFailed(hr);