Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- C++
- CommandList
- 코딩
- vertex
- 다이렉트 초기화
- Direct3D Init
- engine
- DirectX
- View
- RenderTargetView
- DESC
- 동기화
- UE4
- DirectX12
- CPU
- GPU
- 다이렉트X 튜토리얼
- DirectXTK
- swapchain
- 프로그래밍
- C언어
- Direct Init
- 세팅
- 게임수학
- c
- 다중표본화
- Direct3D
- 다이렉트X
- 전처리문
- Input Assembler
Archives
- Today
- Total
비타Cpp
Direct3D Base 클래스 - D3DApp 본문
Direct3D초기화 Windows 메시지 처리, 응용 프로그램의 Main Window 생성, 메시지 루프실행을 위한 메서드를 제공하는 D3DApp이라는 Direct3D 응용 프로그램의 기반(Base) 클래스를 정의 한다.
D3DApp.h
#pragma once
#if defined(DEBUG) || defined(_DEBUG)
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#include "d3dUtil.h"
#include "GameTimer.h"
//d3d12 라이브러리 연결
#pragma comment(lib,"d3dcompiler.lib")
#pragma comment(lib, "D3D12.lib")
#pragma comment(lib, "dxgi.lib")
class D3DApp
{
protected:
D3DApp(HINSTANCE hInstance);
D3DApp(const D3DApp& rhs) = delete;
D3DApp& operator=(const D3DApp& rhs) = delete;
virtual ~D3DApp();
public:
static D3DApp* GetApp();
HINSTANCE AppInst()const;
HWND MainWnd()const;
float AspectRatio()const;
bool Get4xMsaaState()const;
void Set4xMsaaState(bool value);
int Run();
virtual bool Initialize();
virtual LRESULT MsgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
protected:
virtual void CreateRtvAndDsvDescriptorHeaps();
virtual void OnResize();
virtual void Update(const GameTimer& gt) = NULL;
virtual void Draw(const GameTimer& gt) = NULL;
virtual void OnMouseDown(WPARAM btnState, int x, int y) {};
virtual void OnMouseUp(WPARAM btnState, int x, int y) {};
virtual void OnMouseMove(WPARAM btnState, int x, int y) {};
protected:
bool InitMainWindow();
bool InitDirect3D();
void CreateCommandObjects();
void CreateSwapChain();
void FlushCommandQueue();
ID3D12Resource* CurrentBackBuffer()const;
D3D12_CPU_DESCRIPTOR_HANDLE CurrentBackBufferView()const;
D3D12_CPU_DESCRIPTOR_HANDLE DepthStencilView()const;
void CalculateFrameStats();
void LogAdapters();
void LogAdapterOutputs(IDXGIAdapter* adapter);
void LogOutputDisplayModes(IDXGIOutput* output, DXGI_FORMAT format);
protected:
static D3DApp* mApp; //싱글톤을 위한 정적포인터
HINSTANCE mhAppInst = nullptr; // 응용 프로그램 인스턴스 핸들
HWND mhMainWnd = nullptr; // 메인 창 핸들
bool mAppPaused = false; // 응용 프로그램 일시 정지 상태 여부
bool mMinimized = false; // 응용 프로그램 최소화 여부
bool mMaximized = false; // 응용 프로그램 최대화 여부
bool mResizing = false; // 사용자가 크기 조정용 테두리를 드래그 중인지 여부
bool mFullscreenState = false;// 전체화면 활성화 여부
// true로 설정하면 4X MSAA (?.1.8).가 적용. 기본은 false이다.
bool m4xMsaaState = false; // 4X MSAA 활성화 여부
UINT m4xMsaaQuality = 0; // 4X MSAA 품질 수준
// 경과 시간과 게임 전체 시간을 측정하는데 쓰인다.
GameTimer mTimer;
Microsoft::WRL::ComPtr<IDXGIFactory4> mdxgiFactory;
Microsoft::WRL::ComPtr<IDXGISwapChain> mSwapChain;
Microsoft::WRL::ComPtr<ID3D12Device> md3dDevice;
Microsoft::WRL::ComPtr<ID3D12Fence> mFence;
UINT64 mCurrentFence = 0;
Microsoft::WRL::ComPtr<ID3D12CommandQueue> mCommandQueue;
Microsoft::WRL::ComPtr<ID3D12CommandAllocator> mDirectCmdListAlloc;
Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList> mCommandList;
static const int SwapChainBufferCount = 2;
int mCurrBackBuffer = 0;
Microsoft::WRL::ComPtr<ID3D12Resource> mSwapChainBuffer[SwapChainBufferCount];
Microsoft::WRL::ComPtr<ID3D12Resource> mDepthStencilBuffer;
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> mRtvHeap;
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> mDsvHeap;
D3D12_VIEWPORT mScreenViewport;
D3D12_RECT mScissorRect;
UINT mRtvDescriptorSize = 0;
UINT mDsvDescriptorSize = 0;
UINT mCbvSrvUavDescriptorSize = 0;
// 파생 클래스는 자신의 생성자에서 이 멤버 변수들을 목적에 맞는 초기 값들로 설정해야 한다.
std::wstring mMainWndCaption = L"d3d App";
D3D_DRIVER_TYPE md3dDriverType = D3D_DRIVER_TYPE_HARDWARE;
DXGI_FORMAT mBackBufferFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
DXGI_FORMAT mDepthStencilFormat = DXGI_FORMAT_D24_UNORM_S8_UINT;
int mClientWidth = 1080;
int mClientHeight = 720;
};
D3DApp.cpp
#include "d3dApp.h"
#include <windowsx.h>
LRESULT CALLBACK
MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
return D3DApp::GetApp()->MsgProc(hwnd, msg, wParam, lParam);
}
D3DApp* D3DApp::mApp = nullptr;
D3DApp* D3DApp::GetApp()
{
return mApp;
}
D3DApp::D3DApp(HINSTANCE hInstance)
:mhAppInst(hInstance)
{
//싱글톤 객체로 오직 하나만 생성한다.
assert(mApp == nullptr);
mApp = this;
}
D3DApp::~D3DApp()
{
//소멸자는 D3DApp가 획득한 COM 인터페이스들을 해제하고, 명령 대기열을 비운다.
//소멸자에서 명령 대기열을 비우는 이유는,
//GPU가 참조하는 자원들을 안전하게 파괴하려면 GPU가 명령 대기열에 있는 명령들의 처리를 마칠 때까지 기다려야 하기 때문이다.
//그렇게 하지 않으면 응용프로그램 종료 시 GPU가 충돌할 수 있다.
if (md3dDevice != nullptr)
FlushCommandQueue();
}
HINSTANCE D3DApp::AppInst() const
{
return mhAppInst;
}
HWND D3DApp::MainWnd() const
{
return mhMainWnd;
}
float D3DApp::AspectRatio() const
{
//후면버퍼 종횡비
return static_cast<float>(mClientWidth) / mClientHeight;
}
bool D3DApp::Get4xMsaaState() const
{
return m4xMsaaState;
}
void D3DApp::Set4xMsaaState(bool value)
{
if (m4xMsaaState != value)
{
m4xMsaaState = value;
CreateSwapChain();
OnResize();
}
}
int D3DApp::Run()
{
MSG msg = {0};
mTimer.Reset();
while (msg.message != WM_QUIT)
{
//Windows 메시지가 있으면 처리한다.
if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
//없다면 Direct3D 작업(게임) 작업을 수행한다.
else
{
mTimer.Tick();
if (!mAppPaused)
{
CalculateFrameStats();
Update(mTimer);
Draw(mTimer);
}
else
{
Sleep(100);
}
}
}
return (int)msg.wParam;
}
bool D3DApp::Initialize()
{
if (!InitMainWindow())
return false;
if (!InitDirect3D())
return false;
OnResize();
return true;
}
void D3DApp::CreateRtvAndDsvDescriptorHeaps()
{
D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc;
rtvHeapDesc.NumDescriptors = SwapChainBufferCount;
rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE::D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAGS::D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
rtvHeapDesc.NodeMask = 0;
ThrowIfFailed(md3dDevice->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(mRtvHeap.GetAddressOf())));
D3D12_DESCRIPTOR_HEAP_DESC dsvHeapDesc;
dsvHeapDesc.NumDescriptors = 1;
dsvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE::D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
dsvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAGS::D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
dsvHeapDesc.NodeMask = 0;
ThrowIfFailed(md3dDevice->CreateDescriptorHeap(&dsvHeapDesc, IID_PPV_ARGS(mDsvHeap.GetAddressOf())));
}
void D3DApp::OnResize()
{
assert(md3dDevice);
assert(mSwapChain);
assert(mDirectCmdListAlloc);
//리소스를 바꾸기 전에 명령들을 플러쉬해준다.
FlushCommandQueue();
ThrowIfFailed(mCommandList->Reset(mDirectCmdListAlloc.Get(), nullptr));
//이전 리소스 초기화
for (int i = 0; i < SwapChainBufferCount; i++)
{
mSwapChainBuffer[i].Reset();
}
mDepthStencilBuffer.Reset();
//Swap Chain 재설정
ThrowIfFailed(mSwapChain->ResizeBuffers(
SwapChainBufferCount, mClientWidth, mClientHeight, mBackBufferFormat,
DXGI_SWAP_CHAIN_FLAG::DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH));
mCurrBackBuffer = 0;
CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHeapHandle(mRtvHeap->GetCPUDescriptorHandleForHeapStart());
for (UINT i = 0; i < SwapChainBufferCount; i++)
{
ThrowIfFailed(mSwapChain->GetBuffer(i, IID_PPV_ARGS(&mSwapChainBuffer[i])));
md3dDevice->CreateRenderTargetView(mSwapChainBuffer[i].Get(), nullptr, rtvHeapHandle);
rtvHeapHandle.Offset(1, mRtvDescriptorSize);
}
// Depth/Stencil 버퍼와 뷰 생성
D3D12_RESOURCE_DESC depthStencilDesc;
depthStencilDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
depthStencilDesc.Alignment = 0;
depthStencilDesc.Width = mClientWidth;
depthStencilDesc.Height = mClientHeight;
depthStencilDesc.DepthOrArraySize = 1;
depthStencilDesc.MipLevels = 1;
depthStencilDesc.Format = DXGI_FORMAT::DXGI_FORMAT_R24G8_TYPELESS;
depthStencilDesc.SampleDesc.Count = m4xMsaaState ? 4 : 1;
depthStencilDesc.SampleDesc.Quality = m4xMsaaQuality ? (m4xMsaaQuality - 1) : 0;
depthStencilDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
depthStencilDesc.Flags = D3D12_RESOURCE_FLAGS::D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
D3D12_CLEAR_VALUE optClear;
optClear.Format = mDepthStencilFormat;
optClear.DepthStencil.Depth = 1.0f;
optClear.DepthStencil.Stencil = 0;
ThrowIfFailed(md3dDevice->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
D3D12_HEAP_FLAG_NONE,
&depthStencilDesc,
D3D12_RESOURCE_STATE_COMMON,
&optClear,
IID_PPV_ARGS(mDepthStencilBuffer.GetAddressOf())));
//리소스 fomat을 사용하여 전체 리소스의 mip 레벨0의 Desc생성
D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc;
dsvDesc.Flags = D3D12_DSV_FLAG_NONE;
dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
dsvDesc.Format = mDepthStencilFormat;
dsvDesc.Texture2D.MipSlice = 0;
md3dDevice->CreateDepthStencilView(mDepthStencilBuffer.Get(), &dsvDesc, DepthStencilView());
//깊이 버퍼로 사용할 리소스 초기화 상태로 변환
mCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(mDepthStencilBuffer.Get(),
D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_DEPTH_WRITE));
//Resize 실행
ThrowIfFailed(mCommandList->Close());
ID3D12CommandList* cmdsLists[] = { mCommandList.Get() };
mCommandQueue->ExecuteCommandLists(_countof(cmdsLists), cmdsLists);
//resize가 완료될 때 까지 대기.
FlushCommandQueue();
//클라이언트 영역을 뷰포트 변환으로 업데이트
mScreenViewport.TopLeftX = 0;
mScreenViewport.TopLeftY = 0;
mScreenViewport.Width = static_cast<float>(mClientWidth);
mScreenViewport.Height = static_cast<float>(mClientHeight);
mScreenViewport.MinDepth = 0.0f;
mScreenViewport.MaxDepth = 1.0f;
mScissorRect = { 0, 0, mClientWidth, mClientHeight };
}
//메세지 처리부
LRESULT D3DApp::MsgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
// WM_ACTIVATE는 창이 활성화되거나 비활성화 될때 보내집니다.
// 창이 비활성화 되면 게임을 일시정지시키고 활성화되면 시작하도록 해야합니다.
case WM_ACTIVATE:
if(LOWORD(wParam) == WA_INACTIVE)
{
mAppPaused = true;
mTimer.Stop();
}
else
{
mAppPaused = false;
mTimer.Start();
}
return 0;
//WM_SIZE는 창이 Resize될때 보내집니다.
case WM_SIZE:
//새로운 클라이언트 영역을 저장한다.
mClientWidth = LOWORD(lParam);
mClientHeight = HIWORD(lParam);
if (md3dDevice)
{
if (wParam == SIZE_MINIMIZED)
{
mAppPaused = true;
mMinimized = true;
mMaximized = false;
}
else if (wParam == SIZE_MINIMIZED)
{
mAppPaused = false;
mMinimized = false;
mMaximized = true;
OnResize();
}
else if (wParam == SIZE_RESTORED)
{
//최소화 상태에서 다시 복구하는지 여부
if (mMinimized)
{
mAppPaused = false;
mMinimized = false;
OnResize();
}
//최대화 상태에서 다시 복구하는지 여부
else if (mMaximized)
{
mAppPaused = false;
mMaximized = false;
OnResize();
}
else if (mResizing)
{
//
}
else
{
OnResize();
}
}
}
return 0;
// WM_ENTERSIZEMOVE는 resize 바 부분을 집으면 보내집니다.
case WM_ENTERSIZEMOVE:
mAppPaused = true;
mResizing = true;
mTimer.Stop();
return 0;
//WM_EXITSIZEMOVE는 resize 바 부분을 놓으면 보내집니다.
//여기서 새로운 위도우 사이즈를 다시 설정합니다.
case WM_EXITSIZEMOVE:
mAppPaused = false;
mResizing = false;
mTimer.Start();
OnResize();
return 0;
//WM_DESTROY는 윈도우를 종료시킬때 보내집니다.
case WM_DESTROY:
PostQuitMessage(0);
return 0;
// WM_MENUCHAR 메세지는 유저가 활성화 되어 있는 메뉴를 눌렀을때 전송 됩니다.
case WM_MENUCHAR:
//Alt를 입력할때 비프음이 울리지 않습니다.
return MAKELRESULT(0, MNC_CLOSE);
//이 메세지는 창이 너무 작아지는걸 막기위해 보내진다.
case WM_GETMINMAXINFO:
((MINMAXINFO*)lParam)->ptMinTrackSize.x = 200;
((MINMAXINFO*)lParam)->ptMinTrackSize.y = 200;
return 0;
//마우스 키보드 입력상태 메세지들
case WM_LBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_RBUTTONDOWN:
OnMouseDown(wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
return 0;
case WM_LBUTTONUP:
case WM_MBUTTONUP:
case WM_RBUTTONUP:
OnMouseUp(wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
return 0;
case WM_MOUSEMOVE:
OnMouseMove(wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
return 0;
case WM_KEYUP:
if (wParam == VK_ESCAPE)
{
PostQuitMessage(0);
}
else if ((int)wParam == VK_F2)
Set4xMsaaState(!m4xMsaaState);
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
bool D3DApp::InitMainWindow()
{
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = mhAppInst;
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
wc.lpszMenuName = 0;
wc.lpszClassName = L"MainWnd";
if (!RegisterClass(&wc))
{
MessageBox(0, L"RegisterClass Failed.", 0, 0);
return false;
}
//클라이언트 영역만큼 윈도우를 계산
RECT rt = { 0, 0, mClientWidth, mClientHeight };
AdjustWindowRect(&rt, WS_OVERLAPPEDWINDOW, false);
int width = rt.right - rt.left;
int height = rt.bottom - rt.top;
mhMainWnd = CreateWindow(L"MainWnd", mMainWndCaption.c_str(),
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, width, height, 0, 0, mhAppInst, 0);
if (!mhMainWnd)
{
MessageBox(0, L"CreateWindow Failed.", 0, 0);
return false;
}
ShowWindow(mhMainWnd, SW_SHOW);
UpdateWindow(mhMainWnd);
return true;
}
bool D3DApp::InitDirect3D()
{
#if defined(DEBUG) || defined(_DEBUG)
{
ComPtr<ID3D12Debug> debugController;
ThrowIfFailed(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)));
debugController->EnableDebugLayer();
}
#endif
ThrowIfFailed(CreateDXGIFactory1(IID_PPV_ARGS(&mdxgiFactory)));
//하드웨어 장치 생성
HRESULT hardwareResult = D3D12CreateDevice
(nullptr,
D3D_FEATURE_LEVEL_11_0,
IID_PPV_ARGS(&md3dDevice));
//WARP 디바이스
if (FAILED(hardwareResult))
{
ComPtr<IDXGIAdapter> pWarpAdapter;
ThrowIfFailed(mdxgiFactory->EnumWarpAdapter(IID_PPV_ARGS(&pWarpAdapter)));
ThrowIfFailed(D3D12CreateDevice(
pWarpAdapter.Get(),
D3D_FEATURE_LEVEL_11_0,
IID_PPV_ARGS(&md3dDevice)));
}
ThrowIfFailed(md3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE,
IID_PPV_ARGS(&mFence)));
mRtvDescriptorSize = md3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
mDsvDescriptorSize = md3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
mCbvSrvUavDescriptorSize = md3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
//백버퍼에 적용할 4X MSAA 퀄리티 체크.
D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS msQualityLevels;
msQualityLevels.Format = mBackBufferFormat;
msQualityLevels.SampleCount = 4;
msQualityLevels.Flags = D3D12_MULTISAMPLE_QUALITY_LEVELS_FLAG_NONE;
msQualityLevels.NumQualityLevels = 0;
ThrowIfFailed(md3dDevice->CheckFeatureSupport(
D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS,
&msQualityLevels,
sizeof(msQualityLevels)));
m4xMsaaQuality = msQualityLevels.NumQualityLevels;
assert(m4xMsaaQuality > 0 && "Unexpected MSAA quality level.");
#ifdef _DEBUG
LogAdapters();
#endif
CreateCommandObjects();
CreateSwapChain();
CreateRtvAndDsvDescriptorHeaps();
return true;
}
void D3DApp::CreateCommandObjects()
{
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
ThrowIfFailed(md3dDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&mCommandQueue)));
ThrowIfFailed(md3dDevice->CreateCommandAllocator(
D3D12_COMMAND_LIST_TYPE_DIRECT,
IID_PPV_ARGS(mDirectCmdListAlloc.GetAddressOf())));
ThrowIfFailed(md3dDevice->CreateCommandList(
0,
D3D12_COMMAND_LIST_TYPE_DIRECT,
mDirectCmdListAlloc.Get(),
nullptr,
IID_PPV_ARGS(mCommandList.GetAddressOf())));
//Command리스트를 닫고 시작해야한다.
//다시 Command를 작성할때는 닫혀있어야 하기때문이다.
mCommandList->Close();
}
void D3DApp::CreateSwapChain()
{
//다시 만들기 위해, 이전에 있던 스왑체인을 해제한다.
mSwapChain.Reset();
DXGI_SWAP_CHAIN_DESC sd;
sd.BufferDesc.Width = mClientWidth;
sd.BufferDesc.Height = mClientHeight;
sd.BufferDesc.RefreshRate.Numerator = 60;
sd.BufferDesc.RefreshRate.Denominator = 1;
sd.BufferDesc.Format = mBackBufferFormat;
sd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
sd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
sd.SampleDesc.Count = m4xMsaaState ? 4 : 1;
sd.SampleDesc.Quality = m4xMsaaState ? (m4xMsaaQuality - 1) : 0;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.BufferCount = SwapChainBufferCount;
sd.OutputWindow = mhMainWnd;
sd.Windowed = true;
sd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
ThrowIfFailed(mdxgiFactory->CreateSwapChain(
mCommandQueue.Get(),
&sd,
mSwapChain.GetAddressOf()));
}
void D3DApp::FlushCommandQueue()
{
//Advance the fence value to mark commands up to this fence point.
mCurrentFence++;
//Command 큐에 명령을 추가하여 새 Fence 포인트를 설정해야 한다.
//GPU 타임라인에 있어서, Signal() 이전의 모든 명령이 처리될때까지 새 Fence 포인트가 설정되지 않기 때문이다.(무슨말이야!!)
ThrowIfFailed(mCommandQueue->Signal(mFence.Get(), mCurrentFence));
//GPU가 이 Fence 포인트까지 명령을 완료할때까지 기다린다.
if (mFence->GetCompletedValue() < mCurrentFence)
{
HANDLE eventHandle = CreateEventEx(nullptr, false, false, EVENT_ALL_ACCESS);
//GPU가 현재Fence에 도달하면 이벤트가 발생.
ThrowIfFailed(mFence->SetEventOnCompletion(mCurrentFence, eventHandle));
//GPU가 현재Fence에 발생한 이벤트에 도달할 때까지 대기.
WaitForSingleObject(eventHandle, INFINITE);
CloseHandle(eventHandle);
}
}
ID3D12Resource* D3DApp::CurrentBackBuffer() const
{
return mSwapChainBuffer[mCurrBackBuffer].Get();
}
D3D12_CPU_DESCRIPTOR_HANDLE D3DApp::CurrentBackBufferView() const
{
return CD3DX12_CPU_DESCRIPTOR_HANDLE(
mRtvHeap->GetCPUDescriptorHandleForHeapStart(),
mCurrBackBuffer,
mRtvDescriptorSize);
}
D3D12_CPU_DESCRIPTOR_HANDLE D3DApp::DepthStencilView() const
{
return mDsvHeap->GetCPUDescriptorHandleForHeapStart();
}
void D3DApp::CalculateFrameStats()
{
//해당 코드는 초당 프레임을 계산하고, 1프레임 렌더시 걸리는 시간의 평균을 계산한다.
//해당 수치들은 창의 제목표시줄에 추가된다.
static int frameCnt = 0;
static float timeElapsed = 0.0f;
frameCnt++;
//1초동안의 프레임 시간의 평균을 계산합니다.
if ((mTimer.TotalTime() - timeElapsed) >= 1.0f)
{
float fps = (float)frameCnt;
float mspf = 1000.0f / fps;
wstring fpsStr = to_wstring(fps);
wstring mspfStr = to_wstring(mspf);
wstring windowText = mMainWndCaption +
L" fps: " + fpsStr +
L" mspf: " + mspfStr;
SetWindowText(mhMainWnd, windowText.c_str());
//다음 계산을위해 리셋
frameCnt = 0;
timeElapsed += 1.0f;
}
}
void D3DApp::LogAdapters()
{
UINT index = 0;
IDXGIAdapter* adapter = nullptr;
vector<IDXGIAdapter*> adapterList;
while (mdxgiFactory->EnumAdapters(index, &adapter) != DXGI_ERROR_NOT_FOUND)
{
DXGI_ADAPTER_DESC desc;
adapter->GetDesc(&desc);
wstring text = L"***Adapter: ";
text += desc.Description;
text += L"\n";
OutputDebugString(text.c_str());
adapterList.push_back(adapter);
index++;
}
for (size_t i = 0; i < adapterList.size(); i++)
{
LogAdapterOutputs(adapterList[i]);
ReleaseCom(adapterList[i]);
}
}
void D3DApp::LogAdapterOutputs(IDXGIAdapter* adapter)
{
UINT i = 0;
IDXGIOutput* output = nullptr;
while (adapter->EnumOutputs(i, &output) != DXGI_ERROR_NOT_FOUND)
{
DXGI_OUTPUT_DESC desc;
output->GetDesc(&desc);
wstring text = L"***Output: ";
text += desc.DeviceName;
text += L"\n";
OutputDebugString(text.c_str());
LogOutputDisplayModes(output, mBackBufferFormat);
ReleaseCom(output);
i++;
}
}
void D3DApp::LogOutputDisplayModes(IDXGIOutput* output, DXGI_FORMAT format)
{
UINT count = 0;
UINT flags = 0;
// nullptr을 인수로 해서 호출하면 목록의 크기(모드 개수)를 얻게 된다.
output->GetDisplayModeList(format, flags, &count, nullptr);
vector<DXGI_MODE_DESC> modeList(count);
output->GetDisplayModeList(format, flags, &count, &modeList[0]);
for (auto& x : modeList)
{
UINT n = x.RefreshRate.Numerator;
UINT d = x.RefreshRate.Denominator;
wstring text =
L"Width = " + to_wstring(x.Width) + L" " +
L"Height = " + to_wstring(x.Height) + L" " +
L"Refresh = " + to_wstring(n) + L"/" + to_wstring(d) +
L"\n";
OutputDebugString(text.c_str());
}
}
'DirectX12 > 튜토리얼' 카테고리의 다른 글
메시지 처리부(Message Proc) (0) | 2021.12.04 |
---|---|
FPS : Frames Per Second(초당 프레임 수) (0) | 2021.10.16 |
시간 측정 (0) | 2021.10.10 |
Direct3D의 초기화 - 가위 직사각형 설정 (0) | 2021.10.09 |
Direct3D의 초기화 - 뷰포트 생성 (0) | 2021.10.09 |
Comments