1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336
|
// clang-format off
#include "pch.h"
#include "Direct3DBase.h"
// clang-format on
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<ID3D11Device> device;
ComPtr<ID3D11DeviceContext> 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<UINT>(m_renderTargetSize.Width),
static_cast<UINT>(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<UINT>(
m_renderTargetSize.Width); // Match the size of the window.
swapChainDesc.Height = static_cast<UINT>(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<IDXGIDevice1> dxgiDevice;
DX::ThrowIfFailed(m_d3dDevice.As(&dxgiDevice));
ComPtr<IDXGIAdapter> dxgiAdapter;
DX::ThrowIfFailed(dxgiDevice->GetAdapter(&dxgiAdapter));
ComPtr<IDXGIFactory2> 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<IUnknown*>(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<ID3D11Texture2D> 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<UINT>(m_renderTargetSize.Width),
static_cast<UINT>(m_renderTargetSize.Height), 1, 1,
D3D11_BIND_DEPTH_STENCIL);
ComPtr<ID3D11Texture2D> 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 float const 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
}
|