252 строки
8.4 KiB
C++
252 строки
8.4 KiB
C++
//
|
|
// DeviceResources.cpp - A wrapper for the Direct3D 11 device and swapchain
|
|
// (requires DirectX 11.X Xbox One Monolithic Runtime)
|
|
//
|
|
|
|
#include "pch.h"
|
|
#include "DeviceResources.h"
|
|
|
|
using namespace DirectX;
|
|
using namespace DX;
|
|
|
|
using Microsoft::WRL::ComPtr;
|
|
|
|
// Constructor for DeviceResources.
|
|
DeviceResources::DeviceResources(DXGI_FORMAT backBufferFormat, DXGI_FORMAT depthBufferFormat, UINT backBufferCount, unsigned int flags) :
|
|
m_screenViewport{},
|
|
m_backBufferFormat(backBufferFormat),
|
|
m_depthBufferFormat(depthBufferFormat),
|
|
m_backBufferCount(backBufferCount),
|
|
m_window(nullptr),
|
|
m_d3dFeatureLevel(D3D_FEATURE_LEVEL_11_1),
|
|
m_outputSize{0, 0, 1920, 1080},
|
|
m_options(flags)
|
|
{
|
|
}
|
|
|
|
// Configures the Direct3D device, and stores handles to it and the device context.
|
|
void DeviceResources::CreateDeviceResources()
|
|
{
|
|
D3D11X_CREATE_DEVICE_PARAMETERS params = {};
|
|
params.Version = D3D11_SDK_VERSION;
|
|
|
|
#ifdef _DEBUG
|
|
// Enable the debug layer.
|
|
params.Flags = D3D11_CREATE_DEVICE_DEBUG;
|
|
#elif defined(PROFILE)
|
|
// Enable the instrumented driver.
|
|
params.Flags = D3D11_CREATE_DEVICE_INSTRUMENTED;
|
|
#endif
|
|
|
|
if (m_options & c_FastSemantics)
|
|
{
|
|
params.Flags |= D3D11_CREATE_DEVICE_IMMEDIATE_CONTEXT_FAST_SEMANTICS;
|
|
}
|
|
|
|
// Create the Direct3D 11 API device object and a corresponding context.
|
|
ThrowIfFailed(D3D11XCreateDeviceX(
|
|
¶ms,
|
|
m_d3dDevice.ReleaseAndGetAddressOf(),
|
|
m_d3dContext.ReleaseAndGetAddressOf()
|
|
));
|
|
|
|
#ifndef NDEBUG
|
|
ComPtr<ID3D11InfoQueue> d3dInfoQueue;
|
|
if (SUCCEEDED(m_d3dDevice.As(&d3dInfoQueue)))
|
|
{
|
|
#ifdef _DEBUG
|
|
d3dInfoQueue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_CORRUPTION, true);
|
|
d3dInfoQueue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_ERROR, true);
|
|
#endif
|
|
D3D11_MESSAGE_ID hide[] =
|
|
{
|
|
D3D11_MESSAGE_ID_SETPRIVATEDATA_CHANGINGPARAMS,
|
|
};
|
|
D3D11_INFO_QUEUE_FILTER filter = {};
|
|
filter.DenyList.NumIDs = _countof(hide);
|
|
filter.DenyList.pIDList = hide;
|
|
d3dInfoQueue->AddStorageFilterEntries(&filter);
|
|
}
|
|
#endif
|
|
|
|
if (m_options & c_Enable4K_UHD)
|
|
{
|
|
#if _XDK_VER >= 0x3F6803F3 /* XDK Edition 170600 */
|
|
D3D11X_GPU_HARDWARE_CONFIGURATION hwConfig = {};
|
|
m_d3dDevice->GetGpuHardwareConfiguration(&hwConfig);
|
|
if (hwConfig.HardwareVersion >= D3D11X_HARDWARE_VERSION_XBOX_ONE_X)
|
|
{
|
|
m_outputSize = { 0, 0, 3840, 2160 };
|
|
#ifdef _DEBUG
|
|
OutputDebugStringA("INFO: Swapchain using 4k (3840 x 2160) on Xbox One X\n");
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
m_options &= ~c_Enable4K_UHD;
|
|
#ifdef _DEBUG
|
|
OutputDebugStringA("INFO: Swapchain using 1080p (1920 x 1080) on Xbox One or Xbox One S\n");
|
|
#endif
|
|
}
|
|
#else
|
|
m_options &= ~c_Enable4K_UHD;
|
|
#ifdef _DEBUG
|
|
OutputDebugStringA("WARNING: Hardware detection not supported on this XDK edition; Swapchain using 1080p (1920 x 1080)\n");
|
|
#endif
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// These resources need to be recreated every time the window size is changed.
|
|
void DeviceResources::CreateWindowSizeDependentResources()
|
|
{
|
|
if (!m_window)
|
|
{
|
|
throw std::exception("Call SetWindow with a valid CoreWindow pointer");
|
|
}
|
|
|
|
// Clear the previous window size specific context.
|
|
ID3D11RenderTargetView* nullViews[] = {nullptr};
|
|
m_d3dContext->OMSetRenderTargets(_countof(nullViews), nullViews, nullptr);
|
|
m_d3dRenderTargetView.Reset();
|
|
m_d3dDepthStencilView.Reset();
|
|
m_renderTarget.Reset();
|
|
m_depthStencil.Reset();
|
|
m_d3dContext->Flush();
|
|
|
|
// Determine the render target size in pixels.
|
|
UINT backBufferWidth = std::max<UINT>(m_outputSize.right - m_outputSize.left, 1);
|
|
UINT backBufferHeight = std::max<UINT>(m_outputSize.bottom - m_outputSize.top, 1);
|
|
|
|
if (m_swapChain)
|
|
{
|
|
// If the swap chain already exists, resize it.
|
|
ThrowIfFailed(m_swapChain->ResizeBuffers(
|
|
m_backBufferCount,
|
|
backBufferWidth,
|
|
backBufferHeight,
|
|
m_backBufferFormat,
|
|
0
|
|
));
|
|
|
|
// Xbox One apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET.
|
|
}
|
|
else
|
|
{
|
|
// Otherwise, create a new one using the same adapter as the existing Direct3D device.
|
|
|
|
// This sequence obtains the DXGI factory that was used to create the Direct3D device above.
|
|
ComPtr<IDXGIDevice1> dxgiDevice;
|
|
ThrowIfFailed(m_d3dDevice.As(&dxgiDevice));
|
|
|
|
ComPtr<IDXGIAdapter> dxgiAdapter;
|
|
ThrowIfFailed(dxgiDevice->GetAdapter(dxgiAdapter.GetAddressOf()));
|
|
|
|
ComPtr<IDXGIFactory2> dxgiFactory;
|
|
ThrowIfFailed(dxgiAdapter->GetParent(IID_GRAPHICS_PPV_ARGS(dxgiFactory.GetAddressOf())));
|
|
|
|
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
|
|
swapChainDesc.Width = backBufferWidth;
|
|
swapChainDesc.Height = backBufferHeight;
|
|
swapChainDesc.Format = m_backBufferFormat;
|
|
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
|
swapChainDesc.BufferCount = m_backBufferCount;
|
|
swapChainDesc.SampleDesc.Count = 1;
|
|
swapChainDesc.SampleDesc.Quality = 0;
|
|
swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
|
|
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
|
|
swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
|
|
swapChainDesc.Flags = DXGIX_SWAP_CHAIN_FLAG_QUANTIZATION_RGB_FULL;
|
|
|
|
// Create a SwapChain from a CoreWindow.
|
|
ThrowIfFailed(dxgiFactory->CreateSwapChainForCoreWindow(
|
|
m_d3dDevice.Get(),
|
|
m_window,
|
|
&swapChainDesc,
|
|
nullptr,
|
|
m_swapChain.GetAddressOf()
|
|
));
|
|
}
|
|
|
|
// Create a render target view of the swap chain back buffer.
|
|
ThrowIfFailed(m_swapChain->GetBuffer(0, IID_GRAPHICS_PPV_ARGS(m_renderTarget.ReleaseAndGetAddressOf())));
|
|
|
|
m_renderTarget->SetName(L"Render target");
|
|
|
|
ThrowIfFailed(m_d3dDevice->CreateRenderTargetView(
|
|
m_renderTarget.Get(),
|
|
nullptr,
|
|
m_d3dRenderTargetView.ReleaseAndGetAddressOf()
|
|
));
|
|
|
|
if (m_depthBufferFormat != DXGI_FORMAT_UNKNOWN)
|
|
{
|
|
// Create a depth stencil view for use with 3D rendering if needed.
|
|
CD3D11_TEXTURE2D_DESC depthStencilDesc(
|
|
m_depthBufferFormat,
|
|
backBufferWidth,
|
|
backBufferHeight,
|
|
1, // This depth stencil view has only one texture.
|
|
1, // Use a single mipmap level.
|
|
D3D11_BIND_DEPTH_STENCIL
|
|
);
|
|
|
|
ThrowIfFailed(m_d3dDevice->CreateTexture2D(
|
|
&depthStencilDesc,
|
|
nullptr,
|
|
m_depthStencil.ReleaseAndGetAddressOf()
|
|
));
|
|
|
|
CD3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc(D3D11_DSV_DIMENSION_TEXTURE2D);
|
|
ThrowIfFailed(m_d3dDevice->CreateDepthStencilView(
|
|
m_depthStencil.Get(),
|
|
&depthStencilViewDesc,
|
|
m_d3dDepthStencilView.ReleaseAndGetAddressOf()
|
|
));
|
|
}
|
|
|
|
// Set the 3D rendering viewport to target the entire window.
|
|
m_screenViewport = CD3D11_VIEWPORT(
|
|
0.0f,
|
|
0.0f,
|
|
static_cast<float>(backBufferWidth),
|
|
static_cast<float>(backBufferHeight)
|
|
);
|
|
|
|
m_outputSize.left = m_outputSize.top = 0;
|
|
m_outputSize.right = backBufferWidth;
|
|
m_outputSize.bottom = backBufferHeight;
|
|
}
|
|
|
|
// Prepare the render target for rendering.
|
|
void DeviceResources::Prepare()
|
|
{
|
|
if (m_options & c_FastSemantics)
|
|
{
|
|
ThrowIfFailed(m_swapChain->GetBuffer(0, IID_GRAPHICS_PPV_ARGS(m_renderTarget.ReleaseAndGetAddressOf())));
|
|
|
|
m_d3dDevice->PlaceSwapChainView(m_renderTarget.Get(), m_d3dRenderTargetView.Get());
|
|
|
|
m_d3dContext->InsertWaitOnPresent(0, m_renderTarget.Get());
|
|
}
|
|
}
|
|
|
|
// Present the contents of the swap chain to the screen.
|
|
void DeviceResources::Present(UINT decompressFlags)
|
|
{
|
|
if ((m_options & c_FastSemantics) != 0 && decompressFlags != 0)
|
|
{
|
|
m_d3dContext->DecompressResource(
|
|
m_renderTarget.Get(), 0, nullptr,
|
|
m_renderTarget.Get(), 0, nullptr,
|
|
m_backBufferFormat, decompressFlags);
|
|
}
|
|
|
|
// 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.
|
|
ThrowIfFailed(m_swapChain->Present(1, 0));
|
|
|
|
// Xbox One apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET.
|
|
}
|