OSDN Git Service

Release Preview 対応
[shooting3/shootinggame.git] / ShootingGame / DirectXBase.cpp
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.
5 ////
6 //// Copyright (c) Microsoft Corporation. All rights reserved
7
8 #include "pch.h"
9 #include "DirectXBase.h"
10
11 using namespace Windows::UI::Core;
12 using namespace Windows::Foundation;
13 using namespace Microsoft::WRL;
14 using namespace Windows::Graphics::Display;
15 using namespace D2D1;
16
17 // Constructor.
18 DirectXBase::DirectXBase() :
19     m_dpi(-1.0f),
20     m_numBuffers(2)
21 {
22 }
23
24 // Initialize the Direct3D resources required to run.
25 void DirectXBase::Initialize(CoreWindow^ window, float dpi)
26 {
27     m_window = window;
28
29     CreateDeviceIndependentResources();
30     CreateDeviceResources();
31     SetDpi(dpi);
32 }
33
34 // These are the resources required independent of hardware.
35 void DirectXBase::CreateDeviceIndependentResources()
36 {
37     D2D1_FACTORY_OPTIONS options;
38     ZeroMemory(&options, sizeof(D2D1_FACTORY_OPTIONS));
39
40 #if defined(_DEBUG)
41      // If the project is in a debug build, enable Direct2D debugging via SDK Layers
42     options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
43 #endif
44
45     DX::ThrowIfFailed(
46         D2D1CreateFactory(
47             D2D1_FACTORY_TYPE_SINGLE_THREADED,
48             __uuidof(ID2D1Factory1),
49             &options,
50             &m_d2dFactory
51             )
52         );
53
54     DX::ThrowIfFailed(
55         DWriteCreateFactory(
56             DWRITE_FACTORY_TYPE_SHARED,
57             __uuidof(IDWriteFactory),
58             &m_dwriteFactory
59             )
60         );
61
62     DX::ThrowIfFailed(
63         CoCreateInstance(
64             CLSID_WICImagingFactory,
65             nullptr,
66             CLSCTX_INPROC_SERVER,
67             IID_PPV_ARGS(&m_wicFactory)
68             )
69         );
70 }
71
72 // These are the resources that depend on the device.
73 void DirectXBase::CreateDeviceResources()
74 {
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;
79
80 #if defined(_DEBUG)
81     // If the project is in a debug build, enable debugging via SDK Layers with this flag.
82     creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
83 #endif
84
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[] =
90     {
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,
97         D3D_FEATURE_LEVEL_9_1
98     };
99
100     // Create the DX11 API device object, and get a corresponding context.
101     ComPtr<ID3D11Device> device;
102     ComPtr<ID3D11DeviceContext> context;
103     DX::ThrowIfFailed(
104         D3D11CreateDevice(
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
115             )
116         );
117
118     // Get the DirectX11.1 device by QI off the DirectX11 one.
119     DX::ThrowIfFailed(
120         device.As(&m_d3dDevice)
121         );
122
123     // And get the corresponding device context in the same way.
124     DX::ThrowIfFailed(
125         context.As(&m_d3dContext)
126         );
127
128     // Obtain the underlying DXGI device of the Direct3D11.1 device.
129     DX::ThrowIfFailed(
130         m_d3dDevice.As(&dxgiDevice)
131         );
132
133     // Obtain the Direct2D device for 2-D rendering.
134     DX::ThrowIfFailed(
135         m_d2dFactory->CreateDevice(dxgiDevice.Get(), &m_d2dDevice)
136         );
137
138     // And get its corresponding device context object.
139     DX::ThrowIfFailed(
140         m_d2dDevice->CreateDeviceContext(
141             D2D1_DEVICE_CONTEXT_OPTIONS_NONE,
142             &m_d2dContext
143             )
144         );
145
146     // Release the swap chain (if it exists) as it will be incompatible with
147     // the new device.
148     m_swapChain = nullptr;
149 }
150
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)
154 {
155     if (dpi != m_dpi)
156     {
157         // Save the DPI of this display in our class.
158         m_dpi = dpi;
159
160         // Update Direct2D's stored DPI.
161         m_d2dContext->SetDpi(m_dpi, m_dpi);
162
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();
167     }
168 }
169
170 // This routine is called in the event handler for the view SizeChanged event.
171 void DirectXBase::UpdateForWindowSizeChange()
172 {
173     // Only handle window size changed if there is no pending DPI change.
174     if (m_dpi != DisplayProperties::LogicalDpi)
175     {
176         return;
177     }
178
179     if (m_window->Bounds.Width  != m_windowBounds.Width ||
180         m_window->Bounds.Height != m_windowBounds.Height)
181     {
182         m_d2dContext->SetTarget(nullptr);
183         m_d2dTargetBitmap = nullptr;
184         m_renderTargetView = nullptr;
185         m_depthStencilView = nullptr;
186         CreateWindowSizeDependentResources();
187     }
188 }
189
190 // Allocate all memory resources that change on a window SizeChanged event.
191 void DirectXBase::CreateWindowSizeDependentResources()
192 {
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;
196
197     // If the swap chain already exists, resize it.
198     if (m_swapChain != nullptr)
199     {
200         DX::ThrowIfFailed(
201             m_swapChain->ResizeBuffers(m_numBuffers, 0, 0, DXGI_FORMAT_B8G8R8A8_UNORM, 0)
202             );
203     }
204     else    // Otherwise, create a new one.
205     {
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;
219
220         // Once the desired swap chain description is configured, it must be created on the same adapter as our D3D Device
221
222         // First, retrieve the underlying DXGI Device from the D3D Device.
223         ComPtr<IDXGIDevice1> dxgiDevice;
224         DX::ThrowIfFailed(
225             m_d3dDevice.As(&dxgiDevice)
226             );
227
228         // Identify the physical adapter (GPU or card) this device is running on.
229         ComPtr<IDXGIAdapter> dxgiAdapter;
230         DX::ThrowIfFailed(
231             dxgiDevice->GetAdapter(&dxgiAdapter)
232             );
233
234         // And obtain the factory object that created it.
235         ComPtr<IDXGIFactory2> dxgiFactory;
236         DX::ThrowIfFailed(
237             dxgiAdapter->GetParent(IID_PPV_ARGS(&dxgiFactory))
238             );
239
240         // Obtain the final swap chain for this window from the DXGI factory.
241         CoreWindow^ window = m_window.Get();
242         DX::ThrowIfFailed(
243             dxgiFactory->CreateSwapChainForCoreWindow(
244                 m_d3dDevice.Get(),
245                 reinterpret_cast<IUnknown*>(window),
246                 &swapChainDesc,
247                 nullptr,    // allow on all displays
248                 &m_swapChain
249                 )
250             );
251
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.
255         DX::ThrowIfFailed(
256             dxgiDevice->SetMaximumFrameLatency(m_numBuffers - 1)
257             );
258     }
259
260     // Obtain the backbuffer for this window which will be the final 3D rendertarget.
261     ComPtr<ID3D11Texture2D> backBuffer;
262     DX::ThrowIfFailed(
263         m_swapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer))
264         );
265
266     // Create a view interface on the rendertarget to use on bind.
267     DX::ThrowIfFailed(
268         m_d3dDevice->CreateRenderTargetView(
269             backBuffer.Get(),
270             nullptr,
271             &m_renderTargetView
272             )
273         );
274
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);
280
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,
286         1,
287         1,
288         D3D11_BIND_DEPTH_STENCIL
289         );
290
291     // Allocate a 2-D surface as the depth/stencil buffer.
292     ComPtr<ID3D11Texture2D> depthStencil;
293     DX::ThrowIfFailed(
294         m_d3dDevice->CreateTexture2D(
295             &depthStencilDesc,
296             nullptr,
297             &depthStencil
298             )
299         );
300
301     // Create a DepthStencil view on this surface to use on bind.
302     auto viewDesc = CD3D11_DEPTH_STENCIL_VIEW_DESC(D3D11_DSV_DIMENSION_TEXTURE2D);
303     DX::ThrowIfFailed(
304         m_d3dDevice->CreateDepthStencilView(
305             depthStencil.Get(),
306             &viewDesc,
307             &m_depthStencilView
308             )
309         );
310
311     // Create a viewport descriptor of the full window size.
312     CD3D11_VIEWPORT viewport(
313         0.0f,
314         0.0f,
315         static_cast<float>(backBufferDesc.Width),
316         static_cast<float>(backBufferDesc.Height)
317         );
318
319     // Set the current viewport using the descriptor.
320     m_d3dContext->RSSetViewports(1, &viewport);
321
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 =
326         BitmapProperties1(
327             D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
328             PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
329             m_dpi,
330             m_dpi
331             );
332
333     // Direct2D needs the dxgi version of the backbuffer surface pointer.
334     ComPtr<IDXGISurface> dxgiBackBuffer;
335     DX::ThrowIfFailed(
336         m_swapChain->GetBuffer(0, IID_PPV_ARGS(&dxgiBackBuffer))
337         );
338
339     // Get a D2D surface from the DXGI back buffer to use as the D2D render target.
340     DX::ThrowIfFailed(
341         m_d2dContext->CreateBitmapFromDxgiSurface(
342             dxgiBackBuffer.Get(),
343             &bitmapProperties,
344             &m_d2dTargetBitmap
345             )
346         );
347
348     // So now we can set the Direct2D render target.
349     m_d2dContext->SetTarget(m_d2dTargetBitmap.Get());
350
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);
353 }
354
355 // Method to deliver the final image to the display.
356 void DirectXBase::Present()
357 {
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;
365
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, &parameters);
370
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)
374     {
375         Initialize(m_window.Get(), m_dpi);
376     }
377     else
378     {
379         DX::ThrowIfFailed(hr);
380     }
381 }