Committing TBB 2019 Update 8 source code
[platform/upstream/tbb.git] / examples / parallel_for / tachyon / msvs / uwp / DirectXBase.cpp
1 /*
2     Copyright (c) 2005-2019 Intel Corporation
3
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
7
8         http://www.apache.org/licenses/LICENSE-2.0
9
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.
15 */
16
17 #include "pch.h"
18 #include "DirectXBase.h" 
19 #include <windows.ui.xaml.media.dxinterop.h>
20 #include <math.h>
21
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;
27 using namespace D2D1;
28
29 // Constructor.
30 DirectXBase::DirectXBase() :
31     m_dpi(-1.0f)
32 {
33 }
34
35 // Initialize the DirectX resources required to run.
36 void DirectXBase::Initialize(CoreWindow^ window, SwapChainBackgroundPanel^ panel, float dpi)
37 {
38     m_window = window;
39     m_panel = panel;
40
41     CreateDeviceIndependentResources();
42     CreateDeviceResources();
43     SetDpi(dpi);
44 }
45
46 // These are the resources required independent of the device.
47 void DirectXBase::CreateDeviceIndependentResources()
48 {
49     D2D1_FACTORY_OPTIONS options;
50     ZeroMemory(&options, sizeof(D2D1_FACTORY_OPTIONS));
51
52 #if defined(_DEBUG)
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;
56 #endif
57
58     DX::ThrowIfFailed(
59         D2D1CreateFactory(
60             D2D1_FACTORY_TYPE_SINGLE_THREADED,
61             __uuidof(ID2D1Factory1),
62             &options,
63             &m_d2dFactory
64             )
65         );
66
67     DX::ThrowIfFailed(
68         DWriteCreateFactory(
69             DWRITE_FACTORY_TYPE_SHARED,
70             __uuidof(IDWriteFactory),
71             &m_dwriteFactory
72             )
73         );
74
75     DX::ThrowIfFailed(
76         CoCreateInstance(
77             CLSID_WICImagingFactory,
78             nullptr,
79             CLSCTX_INPROC_SERVER,
80             IID_PPV_ARGS(&m_wicFactory)
81             )
82         );
83 }
84
85 // These are the resources that depend on the device.
86 void DirectXBase::CreateDeviceResources()
87 {
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;
92
93 #if defined(_DEBUG)
94     // If the project is in a debug build, enable debugging via SDK Layers with this flag.
95     creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
96 #endif
97
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[] = 
103     {
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
111     };
112
113     // Create the DX11 API device object, and get a corresponding context.
114     ComPtr<ID3D11Device> device;
115     ComPtr<ID3D11DeviceContext> context;
116     DX::ThrowIfFailed(
117         D3D11CreateDevice(
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
128             )
129         );
130     // Get the DirectX11.1 device by QI off the DirectX11 one.
131     DX::ThrowIfFailed(
132         device.As(&m_d3dDevice)
133         );
134
135     // And get the corresponding device context in the same way.
136     DX::ThrowIfFailed(
137         context.As(&m_d3dContext)
138         );
139
140     // Obtain the underlying DXGI device of the Direct3D11.1 device.
141     DX::ThrowIfFailed(
142         m_d3dDevice.As(&dxgiDevice)
143         );
144
145     // Obtain the Direct2D device for 2-D rendering.
146     DX::ThrowIfFailed(
147         m_d2dFactory->CreateDevice(dxgiDevice.Get(), &m_d2dDevice)
148         );
149
150     // And get its corresponding device context object.
151     DX::ThrowIfFailed(
152         m_d2dDevice->CreateDeviceContext(
153             D2D1_DEVICE_CONTEXT_OPTIONS_NONE,
154             &m_d2dContext
155             )
156         );
157
158     // Release the swap chain (if it exists) as it will be incompatible with the new device.
159     m_swapChain = nullptr;
160 }
161
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)
165 {
166     if (dpi != m_dpi)
167     {
168         // Save the DPI of this display in our class.
169         m_dpi = dpi;
170         
171         // Update Direct2D's stored DPI.
172         m_d2dContext->SetDpi(m_dpi, m_dpi);
173
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();
178     }
179 }
180
181 // This routine is called in the event handler for the view SizeChanged event.
182 void DirectXBase::UpdateForWindowSizeChange()
183 {
184     // Only handle window size changed if there is no pending DPI change.
185     if (m_dpi != DisplayInformation::GetForCurrentView()->LogicalDpi)
186         return;
187
188     if (m_window->Bounds.Width  != m_windowBounds.Width ||
189         m_window->Bounds.Height != m_windowBounds.Height)
190     {
191         m_d2dContext->SetTarget(nullptr);
192         m_d2dTargetBitmap = nullptr;
193         m_renderTargetView = nullptr;
194         m_depthStencilView = nullptr;
195         CreateWindowSizeDependentResources();
196     }
197 }
198
199 // Allocate all memory resources that change on a window SizeChanged event.
200 void DirectXBase::CreateWindowSizeDependentResources()
201 {
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;
205
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);
209
210     // If the swap chain already exists, resize it.
211     if (m_swapChain != nullptr)
212     {
213         DX::ThrowIfFailed(
214             m_swapChain->ResizeBuffers(
215                 2,
216                 static_cast<UINT>(m_renderTargetSize.Width),
217                 static_cast<UINT>(m_renderTargetSize.Height),
218                 DXGI_FORMAT_B8G8R8A8_UNORM,
219                 0
220                 )
221             );
222     }
223     // Otherwise, create a new one.
224     else
225     {
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;
239
240         // Once the desired swap chain description is configured, it must be created on the same adapter as our D3D Device.
241
242         // First, retrieve the underlying DXGI Device from the D3D Device.
243         ComPtr<IDXGIDevice1> dxgiDevice;
244         DX::ThrowIfFailed(
245             m_d3dDevice.As(&dxgiDevice)
246             );
247
248         // Identify the physical adapter (GPU or card) this device is running on.
249         ComPtr<IDXGIAdapter> dxgiAdapter;
250         DX::ThrowIfFailed(
251             dxgiDevice->GetAdapter(&dxgiAdapter)
252             );
253
254         // And obtain the factory object that created it.
255         ComPtr<IDXGIFactory2> dxgiFactory;
256         DX::ThrowIfFailed(
257             dxgiAdapter->GetParent(IID_PPV_ARGS(&dxgiFactory))
258             );
259
260         // Create the swap chain and then associate it with the SwapChainBackgroundPanel.
261         DX::ThrowIfFailed(
262             dxgiFactory->CreateSwapChainForComposition(
263                 m_d3dDevice.Get(),
264                 &swapChainDesc,
265                 nullptr,
266                 &m_swapChain
267                 )
268             );
269
270         ComPtr<ISwapChainBackgroundPanelNative> panelNative;
271         DX::ThrowIfFailed(
272             reinterpret_cast<IUnknown*>(m_panel)->QueryInterface(IID_PPV_ARGS(&panelNative))
273             );
274         
275         DX::ThrowIfFailed(
276             panelNative->SetSwapChain(m_swapChain.Get())
277             );
278
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.
282         DX::ThrowIfFailed(
283             dxgiDevice->SetMaximumFrameLatency(1)
284             );
285     }
286
287     // Obtain the backbuffer for this window which will be the final 3D rendertarget.
288     ComPtr<ID3D11Texture2D> backBuffer;
289     DX::ThrowIfFailed(
290         m_swapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer))
291         );
292
293     // Create a view interface on the rendertarget to use on bind.
294     DX::ThrowIfFailed(
295         m_d3dDevice->CreateRenderTargetView(
296             backBuffer.Get(),
297             nullptr,
298             &m_renderTargetView
299             )
300         );
301
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),
307         1,
308         1,
309         D3D11_BIND_DEPTH_STENCIL
310         );
311
312     // Allocate a 2-D surface as the depth/stencil buffer.
313     ComPtr<ID3D11Texture2D> depthStencil;
314     DX::ThrowIfFailed(
315         m_d3dDevice->CreateTexture2D(
316             &depthStencilDesc,
317             nullptr,
318             &depthStencil
319             )
320         );
321
322     // Create a DepthStencil view on this surface to use on bind.
323     CD3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc(D3D11_DSV_DIMENSION_TEXTURE2D);
324     DX::ThrowIfFailed(
325         m_d3dDevice->CreateDepthStencilView(
326             depthStencil.Get(),
327             &depthStencilViewDesc,
328             &m_depthStencilView
329             )
330         );
331
332     // Create a viewport descriptor of the full window size.
333     CD3D11_VIEWPORT viewport(
334         0.0f,
335         0.0f,
336         m_renderTargetSize.Width,
337         m_renderTargetSize.Height
338         );
339
340     // Set the current viewport using the descriptor.
341     m_d3dContext->RSSetViewports(1, &viewport);
342
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 = 
347         BitmapProperties1(
348             D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
349             PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
350             m_dpi,
351             m_dpi
352             );
353
354     // Direct2D needs the DXGI version of the backbuffer surface pointer.
355     ComPtr<IDXGISurface> dxgiBackBuffer;
356     DX::ThrowIfFailed(
357         m_swapChain->GetBuffer(0, IID_PPV_ARGS(&dxgiBackBuffer))
358         );
359
360     // Get a D2D surface from the DXGI back buffer to use as the D2D render target.
361     DX::ThrowIfFailed(
362         m_d2dContext->CreateBitmapFromDxgiSurface(
363             dxgiBackBuffer.Get(),
364             &bitmapProperties,
365             &m_d2dTargetBitmap
366             )
367         );
368
369     // So now we can set the Direct2D render target.
370     m_d2dContext->SetTarget(m_d2dTargetBitmap.Get());
371
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);
374 }
375
376 // Method to deliver the final image to the display.
377 void DirectXBase::Present()
378 {
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;
386     
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, &parameters);
391
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)
395     {
396         Initialize(m_window.Get(), m_panel, m_dpi);
397     }
398     else
399     {
400         DX::ThrowIfFailed(hr);
401     }
402 }
403
404 // Method to convert a length in device-independent pixels (DIPs) to a length in physical pixels.
405 float DirectXBase::ConvertDipsToPixels(float dips)
406 {
407     static const float dipsPerInch = 96.0f;
408     return floor(dips * m_dpi / dipsPerInch + 0.5f); // Round to nearest integer.
409 }