一、性能计时器
(1)基础知识
- 为了制作出精准的动画效果就需要精确地计量时间,特别是要准确地度量出动画每帧画面之间的时间间隔。如果帧率较高,则帧间隔时间就会比较短,因此我们需要使用高精度的计时器。
- 为了精确地度量时间,我们将采用性能计时器。为此我们需要使用头文件。性能计时器返回的时间度量单位叫做计数(count),可通过QueryPerformanceCounter函数来获取性能计时器测量的当前时刻(以计数为单位)。使用QueryPerformanceFrequency函数可以获取性能计时器的频率,其单位为:计数/秒。
(2)代码实现
- 根据以上内容就可以设计我们的性能计时器了,话不多说,都在码里。
- 头文件:
#ifndef GAMETIMER_H
#define GAMETIMER_H
class GameTimer
{
public:
GameTimer();
float TotalTime()const;
float DeltaTime()const;
void Reset();
void Start();
void Stop();
void Tick();
private:
double mSecondsPerCount;
double mDeltaTime;
__int64 mBaseTime;
__int64 mPausedTime;
__int64 mStopTime;
__int64 mPrevTime;
__int64 mCurrTime;
bool mStopped;
};
#endif
#include
#include "GameTimer.h"
GameTimer::GameTimer()
: mSecondsPerCount(0.0), mDeltaTime(-1.0), mBaseTime(0),
mPausedTime(0), mPrevTime(0), mCurrTime(0), mStopped(false)
{
__int64 countsPerSec;
QueryPerformanceFrequency((LARGE_INTEGER*)&countsPerSec);
mSecondsPerCount = 1.0 / (double)countsPerSec;
}
float GameTimer::TotalTime()const
{
if( mStopped )
{
return (float)(((mStopTime - mPausedTime)-mBaseTime)*mSecondsPerCount);
}
else
{
return (float)(((mCurrTime-mPausedTime)-mBaseTime)*mSecondsPerCount);
}
}
float GameTimer::DeltaTime()const
{
return (float)mDeltaTime;
}
void GameTimer::Reset()
{
__int64 currTime;
QueryPerformanceCounter((LARGE_INTEGER*)&currTime);
mBaseTime = currTime;
mPrevTime = currTime;
mStopTime = 0;
mStopped = false;
}
void GameTimer::Start()
{
__int64 startTime;
QueryPerformanceCounter((LARGE_INTEGER*)&startTime);
if( mStopped )
{
mPausedTime += (startTime - mStopTime);
mPrevTime = startTime;
mStopTime = 0;
mStopped = false;
}
}
void GameTimer::Stop()
{
if( !mStopped )
{
__int64 currTime;
QueryPerformanceCounter((LARGE_INTEGER*)&currTime);
mStopTime = currTime;
mStopped = true;
}
}
void GameTimer::Tick()
{
if( mStopped )
{
mDeltaTime = 0.0;
return;
}
__int64 currTime;
QueryPerformanceCounter((LARGE_INTEGER*)&currTime);
mCurrTime = currTime;
mDeltaTime = (mCurrTime - mPrevTime)*mSecondsPerCount;
mPrevTime = mCurrTime;
if(mDeltaTime < 0.0)
{
mDeltaTime = 0.0;
}
}
(3)问题讨论
- 注意的是,按道理来说无论在哪一个处理器上调用QieryPerformanceCounter都应该返回当前时刻的计数值。然而由于基本输入/输出系统(BIOS)或硬件抽象层(HAL)上的缺陷,导致在不同的处理器上可能会得到不同的结果。
- 因此如果应用程序主线程切换到其他处理器上执行命令,则可能会导致帧间隔的波动甚至错误计算,如负帧间隔。
- 我们可以使用SetThreadAffinityMask函数,防止应用程序主线程切换处理器,解决上述问题得到正确的计数差值以及帧间隔。
二、Direct3D应用程序框架示例
头文件 |
源文件 |
d3dApp.h |
d3dApp.cpp |
d3dUtil.h |
d3dUtil.cpp |
- d3dUtil.h和d3dUtil.cpp文件中含有程序所需的实用工具代码。
- d3dApp.h和d3dApp.cpp文件中含有Direct3D应用程序类核心代码。
(1)D3DApp类
- D3DApp类是一种基础的Direct3D应用程序类,它提供了创建应用程序主窗口、运行程序消息循环、处理窗口消息以及初始化Direct3D等多种功能的函数。
- 此外该类还为应用程序例程定义了一组框架函数。
- 我们可以根据需求编写一个继承于D3DApp的类,重写架构的虚函数,以此从D3DApp类中派生出自定义的用户代码。
- D3DApp类的定义如下:
#include "../../Common/d3dUtil.h"
#include "../../Common/GameTimer.h"
#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) = 0;
virtual void Draw(const GameTimer& gt) = 0;
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
{
return mSwapChainBuffer[mCurrBackBuffer].Get();
}
D3D12_CPU_DESCRIPTOR_HANDLE CurrentBackBufferView()const
{
return CD3DX12_CPU_DESCRIPTOR_HANDLE(
mRtvHeap->GetCPUDescriptorHandleForHeapStart(),
mCurrBackBuffer,
mRtvDescriptorSize
);
}
D3D12_CPU_DESCRIPTOR_HANDLE DepthStencilView()const
{
return mDsvHeap->GetCPUDescriptorHandleForHeapStart();
}
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;
bool m4xMsaaState = false;
UINT m4xMsasQuality = 0;
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 = 800;
int mClientHeight = 600;
};
- D3DApp类的非框架方法见龙书4.5.2章即126页。
- D3DApp类的六个框架方法见龙书4.5.3章即127页。
- 除了六个框架方法是虚函数外,D3DApp中还有3个虚函数,它们是用来处理鼠标消息的。
(2)帧的统计信息
- 游戏和图形应用程序往往都会测量每秒渲染的帧数(frames per second,FPS),作为一种画面流畅度的标杆。
- 计算FPS的代码如下:
void D3DApp::CalculateFrameStats()
{
static int frameCnt = 0;
static float timeElapsed = 0.0f;
frameCnt++;
if ((mTimer.TotalTime() - timeElapsed) >= 1.0f)
{
float fps = (float)frameCnt;
float mspf = 1000.0f / fps;
std::wstring fpsStr = std::to_wstring(fps);
std::wstring mspfStr = std::to_wstring(mspf);
std::wstring windowText = mMainWndCaption +
L" fps:" + fpsStr +
L" mspf:" + mspfStr;
SetWindowText(mhMainWnd, windowText.c_str());
frameCnt = 0;
timeElapsed += 1.0f;
}
}
- fps和mspf是衡量渲染效率的两个标准,但是mspf要比fps更具体。mspf能直接地表达渲染一帧所需时间,据mspf的变化我们可以准确地知道渲染每帧所用时间的变化。而fps是非线性的,从1000帧到250帧 和 从100帧到76.9帧,它们渲染每帧的时间都仅变化了3ms,但帧率的变化差距是很大的。
三、完整的Direct3D应用程序框架
#pragma once
#if defined(DEBUG) || defined(_DEBUG)
#define _CRTDBG_MAP_ALLOC
#include
#endif
#include "../../Common/d3dUtil.h"
#include "../../Common/GameTimer.h"
#include
#pragma comment(lib,"d3dcompiler.lib")
#pragma comment(lib, "D3D12.lib")
#pragma comment(lib, "dxgi.lib")
using Microsoft::WRL::ComPtr;
using namespace std;
using namespace DirectX;
LRESULT CALLBACK
MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
class D3DApp
{
protected:
D3DApp(HINSTANCE hInstance)
{
assert(mApp == nullptr);
mApp = this;
}
D3DApp(const D3DApp& rhs) = delete;
D3DApp& operator=(const D3DApp& rhs) = delete;
virtual ~D3DApp()
{
if (md3dDevice != nullptr)
FlushCommandQueue();
}
public:
static D3DApp* GetApp()
{
return mApp;
}
HINSTANCE AppInst()const
{
return mhAppInst;
}
HWND MainWnd()const
{
return mhMainWnd;
}
float AspectRatio()const
{
return static_cast<float>(mClientWidth) / mClientHeight;
}
bool Get4xMsaaState()const
{
return m4xMsaaState;
}
void Set4xMsaaState(bool value)
{
if (m4xMsaaState != value)
{
m4xMsaaState = value;
CreateSwapChain();
OnResize();
}
}
int Run()
{
MSG msg = { 0 };
mTimer.Reset();
while (msg.message != WM_QUIT)
{
if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
mTimer.Tick();
if (!mAppPaused)
{
CalculateFrameStats();
Update(mTimer);
Draw(mTimer);
}
else
{
Sleep(100);
}
}
}
return (int)msg.wParam;
}
virtual bool Initialize()
{
if (!InitMainWindow())
return false;
if (!InitDirect3D())
return false;
OnResize();
return true;
}
virtual LRESULT MsgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_ACTIVATE:
if (LOWORD(wParam) == WA_INACTIVE)
{
mAppPaused = true;
mTimer.Stop();
}
else
{
mAppPaused = false;
mTimer.Start();
}
return 0;
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_MAXIMIZED)
{
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;
case WM_ENTERSIZEMOVE:
mAppPaused = true;
mResizing = true;
mTimer.Stop();
return 0;
case WM_EXITSIZEMOVE:
mAppPaused = false;
mResizing = false;
mTimer.Start();
OnResize();
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_MENUCHAR:
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);
}
protected:
virtual void CreateRtvAndDsvDescriptorHeaps()
{
D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc;
rtvHeapDesc.NumDescriptors = SwapChainBufferCount;
rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
rtvHeapDesc.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_DSV;
dsvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
dsvHeapDesc.NodeMask = 0;
ThrowIfFailed(md3dDevice->CreateDescriptorHeap(
&dsvHeapDesc, IID_PPV_ARGS(mDsvHeap.GetAddressOf())));
}
virtual void 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();
ThrowIfFailed(mSwapChain->ResizeBuffers(
SwapChainBufferCount,
mClientWidth, mClientHeight,
mBackBufferFormat,
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);
}
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 = mDepthStencilFormat;
depthStencilDesc.SampleDesc.Count = m4xMsaaState ? 4 : 1;
depthStencilDesc.SampleDesc.Quality = m4xMsaaState ? (m4xMsaaQuality - 1) : 0;
depthStencilDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
depthStencilDesc.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())
));
md3dDevice->CreateDepthStencilView(
mDepthStencilBuffer.Get(),
nullptr,
DepthStencilView()
);
mCommandList->ResourceBarrier(
1,
&CD3DX12_RESOURCE_BARRIER::Transition(
mDepthStencilBuffer.Get(),
D3D12_RESOURCE_STATE_COMMON,
D3D12_RESOURCE_STATE_DEPTH_WRITE
)
);
ThrowIfFailed(mCommandList->Close());
ID3D12CommandList* cmdsLists[] = { mCommandList.Get() };
mCommandQueue->ExecuteCommandLists(_countof(cmdsLists), cmdsLists);
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 };
}
virtual void Update(const GameTimer& gt) = 0;
virtual void Draw(const GameTimer& gt) = 0;
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()
{
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 R = { 0, 0, mClientWidth, mClientHeight };
AdjustWindowRect(&R, WS_OVERLAPPEDWINDOW, false);
int width = R.right - R.left;
int height = R.bottom - R.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 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_12_0,
IID_PPV_ARGS(&md3dDevice));
if (FAILED(hardwareResult))
{
ComPtr<IDXGIAdapter> pWarpAdapter;
ThrowIfFailed(
mdxgiFactory->EnumWarpAdapter(IID_PPV_ARGS(&pWarpAdapter)));
ThrowIfFailed(D3D12CreateDevice(
pWarpAdapter.Get(),
D3D_FEATURE_LEVEL_12_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);
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 CreateCommandObjects()
{
ComPtr<ID3D12CommandQueue> mCommandQueue;
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)
));
ComPtr<ID3D12CommandAllocator> mCommandAllocator;
ThrowIfFailed(md3dDevice->CreateCommandAllocator(
D3D12_COMMAND_LIST_TYPE_DIRECT,
IID_PPV_ARGS(mCommandAllocator.GetAddressOf())
));
ComPtr<ID3D12GraphicsCommandList> mCommandList;
ThrowIfFailed(md3dDevice->CreateCommandList(
0,
D3D12_COMMAND_LIST_TYPE_DIRECT,
mCommandAllocator.Get(),
nullptr,
IID_PPV_ARGS(mCommandList.GetAddressOf())
));
mCommandList->Close();
}
void 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 = 1;
sd.SampleDesc.Quality = 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 FlushCommandQueue()
{
mCurrentFence++;
ThrowIfFailed(mCommandQueue->Signal(
mFence.Get(), mCurrentFence
));
if (mFence->GetCompletedValue() < mCurrentFence)
{
HANDLE eventHandle = CreateEventEx(nullptr, false, false, EVENT_ALL_ACCESS);
ThrowIfFailed((
mFence->SetEventOnCompletion(mCurrentFence, eventHandle)
));
WaitForSingleObject(eventHandle, INFINITE);
CloseHandle(eventHandle);
}
}
ID3D12Resource* CurrentBackBuffer()const
{
return mSwapChainBuffer[mCurrBackBuffer].Get();
}
D3D12_CPU_DESCRIPTOR_HANDLE CurrentBackBufferView()const
{
return CD3DX12_CPU_DESCRIPTOR_HANDLE(
mRtvHeap->GetCPUDescriptorHandleForHeapStart(),
mCurrBackBuffer,
mRtvDescriptorSize);
}
D3D12_CPU_DESCRIPTOR_HANDLE DepthStencilView()const
{
return mDsvHeap->GetCPUDescriptorHandleForHeapStart();
}
void CalculateFrameStats()
{
static int frameCnt = 0;
static float timeElapsed = 0.0f;
frameCnt++;
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 LogAdapters()
{
UINT i = 0;
IDXGIAdapter* adapter = nullptr;
std::vector<IDXGIAdapter*> adapterList;
while (mdxgiFactory->EnumAdapters(i, &adapter) != DXGI_ERROR_NOT_FOUND)
{
DXGI_ADAPTER_DESC desc;
adapter->GetDesc(&desc);
std::wstring text = L"***Adapter: ";
text += desc.Description;
text += L"\n";
OutputDebugString(text.c_str());
adapterList.push_back(adapter);
++i;
}
for (size_t i = 0; i < adapterList.size(); ++i)
{
LogAdapterOutputs(adapterList[i]);
ReleaseCom(adapterList[i]);
}
}
void 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);
std::wstring text = L"***Output: ";
text += desc.DeviceName;
text += L"\n";
OutputDebugString(text.c_str());
LogOutputDisplayModes(output, mBackBufferFormat);
ReleaseCom(output);
++i;
}
}
void LogOutputDisplayModes(IDXGIOutput* output, DXGI_FORMAT format)
{
UINT count = 0;
UINT flags = 0;
output->GetDisplayModeList(format, flags, &count, nullptr);
std::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;
std::wstring text =
L"Width = " + std::to_wstring(x.Width) + L" " +
L"Height = " + std::to_wstring(x.Height) + L" " +
L"Refresh = " + std::to_wstring(n) + L"/" + std::to_wstring(d) +
L"\n";
::OutputDebugString(text.c_str());
}
}
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;
bool m4xMsaaState = false;
UINT m4xMsaaQuality = 0;
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 = 800;
int mClientHeight = 600;
};
LRESULT CALLBACK
MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
return D3DApp::GetApp()->MsgProc(hwnd, msg, wParam, lParam);
}