2 Copyright (c) 2005-2019 Intel Corporation
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
8 http://www.apache.org/licenses/LICENSE-2.0
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
18 #include "DirectXBase.h"
19 #include <windows.ui.xaml.media.dxinterop.h>
22 using namespace Microsoft::WRL;
23 using namespace Windows::UI::Core;
24 using namespace Windows::UI::Xaml::Controls;
25 using namespace Windows::Foundation;
26 using namespace Windows::Graphics::Display;
30 DirectXBase::DirectXBase() :
35 // Initialize the DirectX resources required to run.
36 void DirectXBase::Initialize(CoreWindow^ window, SwapChainBackgroundPanel^ panel, float dpi)
41 CreateDeviceIndependentResources();
42 CreateDeviceResources();
46 // These are the resources required independent of the device.
47 void DirectXBase::CreateDeviceIndependentResources()
49 D2D1_FACTORY_OPTIONS options;
50 ZeroMemory(&options, sizeof(D2D1_FACTORY_OPTIONS));
53 // If the project is in a debug build, enable Direct2D debugging via SDK Layers
54 // TODO: investigate resource leaks reported by a debug version of a device.
55 // options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
60 D2D1_FACTORY_TYPE_SINGLE_THREADED,
61 __uuidof(ID2D1Factory1),
69 DWRITE_FACTORY_TYPE_SHARED,
70 __uuidof(IDWriteFactory),
77 CLSID_WICImagingFactory,
80 IID_PPV_ARGS(&m_wicFactory)
85 // These are the resources that depend on the device.
86 void DirectXBase::CreateDeviceResources()
88 // This flag adds support for surfaces with a different color channel ordering than the API default.
89 // It is recommended usage, and is required for compatibility with Direct2D.
90 UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
91 ComPtr<IDXGIDevice> dxgiDevice;
94 // If the project is in a debug build, enable debugging via SDK Layers with this flag.
95 creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
98 // This array defines the set of DirectX hardware feature levels this app will support.
99 // Note the ordering should be preserved.
100 // Don't forget to declare your application's minimum required feature level in its
101 // description. All applications are assumed to support 9.1 unless otherwise stated.
102 D3D_FEATURE_LEVEL featureLevels[] =
104 D3D_FEATURE_LEVEL_11_1,
105 D3D_FEATURE_LEVEL_11_0,
106 D3D_FEATURE_LEVEL_10_1,
107 D3D_FEATURE_LEVEL_10_0,
108 D3D_FEATURE_LEVEL_9_3,
109 D3D_FEATURE_LEVEL_9_2,
110 D3D_FEATURE_LEVEL_9_1
113 // Create the DX11 API device object, and get a corresponding context.
114 ComPtr<ID3D11Device> device;
115 ComPtr<ID3D11DeviceContext> context;
118 nullptr, // specify null to use the default adapter
119 D3D_DRIVER_TYPE_HARDWARE,
120 0, // leave as 0 unless software device
121 creationFlags, // optionally set debug and Direct2D compatibility flags
122 featureLevels, // list of feature levels this app can support
123 ARRAYSIZE(featureLevels), // number of entries in above list
124 D3D11_SDK_VERSION, // always set this to D3D11_SDK_VERSION for Metro style apps
125 &device, // returns the Direct3D device created
126 &m_featureLevel, // returns feature level of device created
127 &context // returns the device immediate context
130 // Get the DirectX11.1 device by QI off the DirectX11 one.
132 device.As(&m_d3dDevice)
135 // And get the corresponding device context in the same way.
137 context.As(&m_d3dContext)
140 // Obtain the underlying DXGI device of the Direct3D11.1 device.
142 m_d3dDevice.As(&dxgiDevice)
145 // Obtain the Direct2D device for 2-D rendering.
147 m_d2dFactory->CreateDevice(dxgiDevice.Get(), &m_d2dDevice)
150 // And get its corresponding device context object.
152 m_d2dDevice->CreateDeviceContext(
153 D2D1_DEVICE_CONTEXT_OPTIONS_NONE,
158 // Release the swap chain (if it exists) as it will be incompatible with the new device.
159 m_swapChain = nullptr;
162 // Helps track the DPI in the helper class.
163 // This is called in the dpiChanged event handler in the view class.
164 void DirectXBase::SetDpi(float dpi)
168 // Save the DPI of this display in our class.
171 // Update Direct2D's stored DPI.
172 m_d2dContext->SetDpi(m_dpi, m_dpi);
174 // Often a DPI change implies a window size change. In some cases Windows will issues
175 // both a size changed event and a DPI changed event. In this case, the resulting bounds
176 // will not change, and the window resize code will only be executed once.
177 UpdateForWindowSizeChange();
181 // This routine is called in the event handler for the view SizeChanged event.
182 void DirectXBase::UpdateForWindowSizeChange()
184 // Only handle window size changed if there is no pending DPI change.
185 if (m_dpi != DisplayInformation::GetForCurrentView()->LogicalDpi)
188 if (m_window->Bounds.Width != m_windowBounds.Width ||
189 m_window->Bounds.Height != m_windowBounds.Height)
191 m_d2dContext->SetTarget(nullptr);
192 m_d2dTargetBitmap = nullptr;
193 m_renderTargetView = nullptr;
194 m_depthStencilView = nullptr;
195 CreateWindowSizeDependentResources();
199 // Allocate all memory resources that change on a window SizeChanged event.
200 void DirectXBase::CreateWindowSizeDependentResources()
202 // Store the window bounds so the next time we get a SizeChanged event we can
203 // avoid rebuilding everything if the size is identical.
204 m_windowBounds = m_window->Bounds;
206 // Calculate the necessary swap chain and render target size in pixels.
207 m_renderTargetSize.Width = ConvertDipsToPixels(m_windowBounds.Width);
208 m_renderTargetSize.Height = ConvertDipsToPixels(m_windowBounds.Height);
210 // If the swap chain already exists, resize it.
211 if (m_swapChain != nullptr)
214 m_swapChain->ResizeBuffers(
216 static_cast<UINT>(m_renderTargetSize.Width),
217 static_cast<UINT>(m_renderTargetSize.Height),
218 DXGI_FORMAT_B8G8R8A8_UNORM,
223 // Otherwise, create a new one.
226 // Allocate a descriptor.
227 DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0};
228 swapChainDesc.Width = static_cast<UINT>(m_renderTargetSize.Width); // Match the size of the windowm.
229 swapChainDesc.Height = static_cast<UINT>(m_renderTargetSize.Height);
230 swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // This is the most common swapchain format.
231 swapChainDesc.Stereo = false;
232 swapChainDesc.SampleDesc.Count = 1; // Don't use multi-sampling.
233 swapChainDesc.SampleDesc.Quality = 0;
234 swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
235 swapChainDesc.BufferCount = 2; // Use double buffering to enable flip.
236 swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
237 swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // All Metro style apps must use this SwapEffect.
238 swapChainDesc.Flags = 0;
240 // Once the desired swap chain description is configured, it must be created on the same adapter as our D3D Device.
242 // First, retrieve the underlying DXGI Device from the D3D Device.
243 ComPtr<IDXGIDevice1> dxgiDevice;
245 m_d3dDevice.As(&dxgiDevice)
248 // Identify the physical adapter (GPU or card) this device is running on.
249 ComPtr<IDXGIAdapter> dxgiAdapter;
251 dxgiDevice->GetAdapter(&dxgiAdapter)
254 // And obtain the factory object that created it.
255 ComPtr<IDXGIFactory2> dxgiFactory;
257 dxgiAdapter->GetParent(IID_PPV_ARGS(&dxgiFactory))
260 // Create the swap chain and then associate it with the SwapChainBackgroundPanel.
262 dxgiFactory->CreateSwapChainForComposition(
270 ComPtr<ISwapChainBackgroundPanelNative> panelNative;
272 reinterpret_cast<IUnknown*>(m_panel)->QueryInterface(IID_PPV_ARGS(&panelNative))
276 panelNative->SetSwapChain(m_swapChain.Get())
279 // Ensure that DXGI does not queue more than one frame at a time. This both reduces
280 // latency and ensures that the application will only render after each VSync, minimizing
281 // power consumption.
283 dxgiDevice->SetMaximumFrameLatency(1)
287 // Obtain the backbuffer for this window which will be the final 3D rendertarget.
288 ComPtr<ID3D11Texture2D> backBuffer;
290 m_swapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer))
293 // Create a view interface on the rendertarget to use on bind.
295 m_d3dDevice->CreateRenderTargetView(
302 // Create a descriptor for the depth/stencil buffer.
303 CD3D11_TEXTURE2D_DESC depthStencilDesc(
304 DXGI_FORMAT_D24_UNORM_S8_UINT,
305 static_cast<UINT>(m_renderTargetSize.Width),
306 static_cast<UINT>(m_renderTargetSize.Height),
309 D3D11_BIND_DEPTH_STENCIL
312 // Allocate a 2-D surface as the depth/stencil buffer.
313 ComPtr<ID3D11Texture2D> depthStencil;
315 m_d3dDevice->CreateTexture2D(
322 // Create a DepthStencil view on this surface to use on bind.
323 CD3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc(D3D11_DSV_DIMENSION_TEXTURE2D);
325 m_d3dDevice->CreateDepthStencilView(
327 &depthStencilViewDesc,
332 // Create a viewport descriptor of the full window size.
333 CD3D11_VIEWPORT viewport(
336 m_renderTargetSize.Width,
337 m_renderTargetSize.Height
340 // Set the current viewport using the descriptor.
341 m_d3dContext->RSSetViewports(1, &viewport);
343 // Now we set up the Direct2D render target bitmap linked to the swapchain.
344 // Whenever we render to this bitmap, it will be directly rendered to the
345 // swapchain associated with the window.
346 D2D1_BITMAP_PROPERTIES1 bitmapProperties =
348 D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
349 PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
354 // Direct2D needs the DXGI version of the backbuffer surface pointer.
355 ComPtr<IDXGISurface> dxgiBackBuffer;
357 m_swapChain->GetBuffer(0, IID_PPV_ARGS(&dxgiBackBuffer))
360 // Get a D2D surface from the DXGI back buffer to use as the D2D render target.
362 m_d2dContext->CreateBitmapFromDxgiSurface(
363 dxgiBackBuffer.Get(),
369 // So now we can set the Direct2D render target.
370 m_d2dContext->SetTarget(m_d2dTargetBitmap.Get());
372 // Set D2D text anti-alias mode to Grayscale to ensure proper rendering of text on intermediate surfaces.
373 m_d2dContext->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE);
376 // Method to deliver the final image to the display.
377 void DirectXBase::Present()
379 // The application may optionally specify "dirty" or "scroll" rects to improve efficiency
380 // in certain scenarios.
381 DXGI_PRESENT_PARAMETERS parameters = {0};
382 parameters.DirtyRectsCount = 0;
383 parameters.pDirtyRects = nullptr;
384 parameters.pScrollRect = nullptr;
385 parameters.pScrollOffset = nullptr;
387 // The first argument instructs DXGI to block until VSync, putting the application
388 // to sleep until the next VSync. This ensures we don't waste any cycles rendering
389 // frames that will never be displayed to the screen.
390 HRESULT hr = m_swapChain->Present1(1, 0, ¶meters);
392 // If the device was removed either by a disconnect or a driver upgrade, we
393 // must completely reinitialize the renderer.
394 if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET)
396 Initialize(m_window.Get(), m_panel, m_dpi);
400 DX::ThrowIfFailed(hr);
404 // Method to convert a length in device-independent pixels (DIPs) to a length in physical pixels.
405 float DirectXBase::ConvertDipsToPixels(float dips)
407 static const float dipsPerInch = 96.0f;
408 return floor(dips * m_dpi / dipsPerInch + 0.5f); // Round to nearest integer.