Method 중 'Get'으로 시작하거나 'Is'로 시작하는 method, 'Set'으로 시작하는 method들은 설명을 생략하겠다.
이들은 대체로 field들의 Getter, Setter 함수들이기에 값을 받아오거나 설정하는 기능으로만 기억하면 된다.
중요한 것은 이런 method가 아니라 그것이 가르키는 field가 무엇인지 아니겠는가?
#include "pch.h"
#include "DeviceResources.h"
#include "DirectXHelper.h"
using namespace DirectX;
using namespace Microsoft::WRL;
using namespace Windows::Foundation;
using namespace Windows::Graphics::Display;
using namespace Windows::UI::Core;
using namespace Windows::UI::Xaml::Controls;
using namespace Platform;
namespace DisplayMetrics
{
// High resolution displays can require a lot of GPU and battery power to render.
// High resolution phones, for example, may suffer from poor battery life if
// games attempt to render at 60 frames per second at full fidelity.
// The decision to render at full fidelity across all platforms and form factors
// should be deliberate.
static const bool SupportHighResolutions = false;
// The default thresholds that define a "high resolution" display. If the thresholds
// are exceeded and SupportHighResolutions is false, the dimensions will be scaled
// by 50%.
static const float DpiThreshold = 192.0f; // 200% of standard desktop display.
static const float WidthThreshold = 1920.0f; // 1080p width.
static const float HeightThreshold = 1080.0f; // 1080p height.
};
// Constants used to calculate screen rotations.
namespace ScreenRotation
{
// 0-degree Z-rotation
static const XMFLOAT4X4 Rotation0(
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
);
// 90-degree Z-rotation
static const XMFLOAT4X4 Rotation90(
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
);
// 180-degree Z-rotation
static const XMFLOAT4X4 Rotation180(
-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
);
// 270-degree Z-rotation
static const XMFLOAT4X4 Rotation270(
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
);
};
DeviceResources.cpp에서 header나 namespace 선언, 상수 선언 부분만 가져온 것이다.
먼저 DisplayMatrics namespace 안에 선언된 상수들을 살펴보자.
SupportHighResolutions는 High Resolution을 지원하는지를 판단하는 변수이다.
High Resolution은 많은 GPU resource와 전력을 소모하는 작업이다.
그렇기에 예시에서는 이를 지원하지 않는 것으로 결정 한 것 같다.
WidthThreshold, HeightThreshold는 화면의 너비와 높이 크기 제한을 결정한다.
DpiThreshold 역시 해상도의 제한을 결정하며, 192.0은 보통 데스크탑 화면의 200% 정도이다.
ScreenRotation은 4×4 Matrix 상에서 Z축을 기준으로 0, 90, 180, 360 회전 연산을 Matrix로 표현한 것이다.
이 중 다시 한번 낯선 것이 있다.
Header에 보면 [DirectXHelper.h]라는 header 파일이 보일 것이다.
DirectXHelper.h는 Microsoft에서 제공하는 DirectX 개발을 지원하는 코드이다.
#pragma once
#include <ppltasks.h> // For create_task
namespace DX
{
inline void ThrowIfFailed(HRESULT hr)
{
if (FAILED(hr))
{
// Set a breakpoint on this line to catch Win32 API errors.
throw Platform::Exception::CreateException(hr);
}
}
// Function that reads from a binary file asynchronously.
inline Concurrency::task<std::vector<byte>> ReadDataAsync(const std::wstring& filename)
{
using namespace Windows::Storage;
using namespace Concurrency;
auto folder = Windows::ApplicationModel::Package::Current->InstalledLocation;
return create_task(folder->GetFileAsync(Platform::StringReference(filename.c_str()))).then([](StorageFile^ file)
{
return FileIO::ReadBufferAsync(file);
}).then([](Streams::IBuffer^ fileBuffer) -> std::vector<byte>
{
std::vector<byte> returnBuffer;
returnBuffer.resize(fileBuffer->Length);
Streams::DataReader::FromBuffer(fileBuffer)->ReadBytes(Platform::ArrayReference<byte>(returnBuffer.data(), fileBuffer->Length));
return returnBuffer;
});
}
// Converts a length in device-independent pixels (DIPs) to a length in physical pixels.
inline float ConvertDipsToPixels(float dips, float dpi)
{
static const float dipsPerInch = 96.0f;
return floorf(dips * dpi / dipsPerInch + 0.5f); // Round to nearest integer.
}
// Assign a name to the object to aid with debugging.
#if defined(_DEBUG)
inline void SetName(ID3D12Object* pObject, LPCWSTR name)
{
pObject->SetName(name);
}
#else
inline void SetName(ID3D12Object*, LPCWSTR)
{
}
#endif
}
// Naming helper function for ComPtr<T>.
// Assigns the name of the variable as the name of the object.
#define NAME_D3D12_OBJECT(x) DX::SetName(x.Get(), L#x)
ThrowIfFailed는 DirectX Win32 API에서 반환된 오류 HRESULT 값을 Windows Runtime Exception으로 변환한다.
Resource는 두가지로 분류된다. Direct3D device와 독립적인 것과, 연관된 것.
본 예시는 DirectX12를 사용하기 대문에 독립적인 부분인 CreateDeviceIndependentResources는 빈 함수이다.
그렇다면 남은 함수인 CreateDeviceResources를 보자.
// Configures the Direct3D device, and stores handles to it and the device context.
void DX::DeviceResources::CreateDeviceResources()
{
#if defined(_DEBUG)
// If the project is in a debug build, enable debugging via SDK Layers.
{
ComPtr<ID3D12Debug> debugController;
if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController))))
{
debugController->EnableDebugLayer();
}
}
#endif
DX::ThrowIfFailed(CreateDXGIFactory1(IID_PPV_ARGS(&m_dxgiFactory)));
ComPtr<IDXGIAdapter1> adapter;
GetHardwareAdapter(&adapter);
// Create the Direct3D 12 API device object
HRESULT hr = D3D12CreateDevice(
adapter.Get(), // The hardware adapter.
D3D_FEATURE_LEVEL_11_0, // Minimum feature level this app can support.
IID_PPV_ARGS(&m_d3dDevice) // Returns the Direct3D device created.
);
#if defined(_DEBUG)
if (FAILED(hr))
{
// If the initialization fails, fall back to the WARP device.
// For more information on WARP, see:
// https://go.microsoft.com/fwlink/?LinkId=286690
ComPtr<IDXGIAdapter> warpAdapter;
DX::ThrowIfFailed(m_dxgiFactory->EnumWarpAdapter(IID_PPV_ARGS(&warpAdapter)));
hr = D3D12CreateDevice(warpAdapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_d3dDevice));
}
#endif
DX::ThrowIfFailed(hr);
// Create the command queue.
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
DX::ThrowIfFailed(m_d3dDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&m_commandQueue)));
NAME_D3D12_OBJECT(m_commandQueue);
// Create descriptor heaps for render target views and depth stencil views.
D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {};
rtvHeapDesc.NumDescriptors = c_frameCount;
rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
DX::ThrowIfFailed(m_d3dDevice->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&m_rtvHeap)));
NAME_D3D12_OBJECT(m_rtvHeap);
m_rtvDescriptorSize = m_d3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
D3D12_DESCRIPTOR_HEAP_DESC dsvHeapDesc = {};
dsvHeapDesc.NumDescriptors = 1;
dsvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
dsvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
ThrowIfFailed(m_d3dDevice->CreateDescriptorHeap(&dsvHeapDesc, IID_PPV_ARGS(&m_dsvHeap)));
NAME_D3D12_OBJECT(m_dsvHeap);
for (UINT n = 0; n < c_frameCount; n++)
{
DX::ThrowIfFailed(
m_d3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&m_commandAllocators[n]))
);
}
// Create synchronization objects.
DX::ThrowIfFailed(m_d3dDevice->CreateFence(m_fenceValues[m_currentFrame], D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_fence)));
m_fenceValues[m_currentFrame]++;
m_fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
if (m_fenceEvent == nullptr)
{
DX::ThrowIfFailed(HRESULT_FROM_WIN32(GetLastError()));
}
}
간단하게 설명을 하자면
1) DXGIFactory1을 생성하면서 실패 시 Error를 throw 한다.
2) 1)이 문제 없이 동작하면 Comptr<IDXGIAdapter1>에 GetHardwareAdapter의 반환 값을 저장한다.
GetHardWareAdpater는 Direct3D 12를 제공하는 사용 가능한 Hardware Adapter 중 첫번째를 반환한다.
// This method acquires the first available hardware adapter that supports Direct3D 12.
// If no such adapter can be found, *ppAdapter will be set to nullptr.
void DX::DeviceResources::GetHardwareAdapter(IDXGIAdapter1** ppAdapter)
{
ComPtr<IDXGIAdapter1> adapter;
*ppAdapter = nullptr;
for (UINT adapterIndex = 0; DXGI_ERROR_NOT_FOUND != m_dxgiFactory->EnumAdapters1(adapterIndex, &adapter); adapterIndex++)
{
DXGI_ADAPTER_DESC1 desc;
adapter->GetDesc1(&desc);
if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)
{
// Don't select the Basic Render Driver adapter.
continue;
}
// Check to see if the adapter supports Direct3D 12, but don't create the
// actual device yet.
if (SUCCEEDED(D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), nullptr)))
{
break;
}
}
*ppAdapter = adapter.Detach();
}
3) 2)에서 저장한 HardwareAdapter와 이 Application이 제공 할 최소 Direct3D feature level, 생성 될 Direct3D device를 저장 할 변수를 넣어 Direct3D 12 API Device Object를 생성한다.