一、性能计时器
(1)基础知识
为了制作出精准的动画效果 就需要精确地计量时间 ,特别是要准确地度量 出动画每帧画面之间的时间间隔 。如果帧率较高,则帧间隔时间就会比较短 ,因此我们需要使用高精度的计时器 。 为了精确地度量时间,我们将采用性能计时器 。为此我们需要使用头文件<window.h> 。性能计时器返回的时间度量单位叫做计数(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 <windows.h>
# 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 <crtdbg.h>
# endif
# include "../../Common/d3dUtil.h"
# include "../../Common/GameTimer.h"
# include <WindowsX.h>
# 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) ;
}