You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

337 lines
12 KiB

// 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, &parameters);
#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 const float 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
}