初始化工作在上一篇中其实已经结束,这篇主要是重构代码。我们将新建两个头文件以及他们的.cpp文件。其中一个命名为ToolFunc,主要声明和实现ThrowIfFailed宏的一系列类和函数。另一个命名为D3D12App,主要声明和实现D3D12App类及其函数,是我们这次封装的重中之重。
首先是ToolFunc.h和ToolFunc.cpp,这个比较简单,直接复制之前的工具类和函数即可,需要注意的是,宏定义和内联函数一样可以在头文件中直接定义。由于D3D12APP类也将引用这个文件,所以还要将所有的包含头文件拷入。头文件代码如下所示:
#pragma once
#include <Windows.h>
#include <wrl.h>
#include <dxgi1_4.h>
#include <d3d12.h>
#include <d3dcompiler.h>
#include <DirectXMath.h>
#include <DirectXPackedVector.h>
#include <DirectXColors.h>
#include <DirectXCollision.h>
#include <string>
#include <memory>
#include <algorithm>
#include <vector>
#include <array>
#include <unordered_map>
#include <cstdint>
#include <fstream>
#include <sstream>
#include <windowsx.h>
#include <comdef.h>
//AnsiToWString函数(转换成宽字符类型的字符串,wstring)
//在Windows平台上,我们应该都使用wstring和wchar_t,处理方式是在字符串前+L
inline std::wstring AnsiToWString(const std::string& str)
{
WCHAR buffer[512];
MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, buffer, 512);
return std::wstring(buffer);
}
class DxException
{
public:
DxException() = default;
DxException(HRESULT hr, const std::wstring& functionName, const std::wstring& filename, int lineNumber);
std::wstring ToString()const;
HRESULT ErrorCode = S_OK;
std::wstring FunctionName;
std::wstring Filename;
int LineNumber = -1;
};
//宏定义ThrowIfFailed
#ifndef ThrowIfFailed
#define ThrowIfFailed(x)
{
HRESULT hr__ = (x);
std::wstring wfn = AnsiToWString(__FILE__);
if(FAILED(hr__)) { throw DxException(hr__, L#x, wfn, __LINE__); }
}
#endif
接下来我们新建D3D12App.h文件,然后新建一个类,创建构造和析构后,基本就是将之前声明的函数和变量复制进去,需要注意的是,Draw函数不属于初始化范畴,所以将其定义成虚函数(其实包括Init在内的众多初始化函数也应该定义成虚函数,由于这次案例比较简单,所以没定义,之后碰到实际问题再实际处理)。头文件代码如下:
#pragma once
#include "ToolFunc.h"
#include "GameTime.h"
#include "d3dx12.h"
using namespace Microsoft::WRL;
#pragma comment(lib, "d3dcompiler.lib")
#pragma comment(lib, "D3D12.lib")
#pragma comment(lib, "dxgi.lib")
class D3D12App
{
protected:
D3D12App();
virtual ~D3D12App();
public:
int Run();
bool Init(HINSTANCE hInstance, int nShowCmd);
bool InitWindow(HINSTANCE hInstance, int nShowCmd);
bool InitDirect3D();
virtual void Draw();
void CreateDevice();
void CreateFence();
void GetDescriptorSize();
void SetMSAA();
void CreateCommandObject();
void CreateSwapChain();
void CreateDescriptorHeap();
void CreateRTV();
void CreateDSV();
void CreateViewPortAndScissorRect();
void FlushCmdQueue();
void CalculateFrameState();
protected:
HWND mhMainWnd = 0;
//指针接口和变量声明
ComPtr<ID3D12Device> d3dDevice;
ComPtr<IDXGIFactory4> dxgiFactory;
ComPtr<ID3D12Fence> fence;
ComPtr<ID3D12CommandAllocator> cmdAllocator;
ComPtr<ID3D12CommandQueue> cmdQueue;
ComPtr<ID3D12GraphicsCommandList> cmdList;
ComPtr<ID3D12Resource> depthStencilBuffer;
ComPtr<ID3D12Resource> swapChainBuffer[2];
ComPtr<IDXGISwapChain> swapChain;
ComPtr<ID3D12DescriptorHeap> rtvHeap;
ComPtr<ID3D12DescriptorHeap> dsvHeap;
D3D12_VIEWPORT viewPort;
D3D12_RECT scissorRect;
UINT rtvDescriptorSize = 0;
UINT dsvDescriptorSize = 0;
UINT cbv_srv_uavDescriptorSize = 0;
//GameTime类实例声明
GameTime gt;
UINT mCurrentBackBuffer = 0;
};
在D3D12App.cpp文件的实现中,要注意MainWndProc函数是个回调函数,在创建窗口时会调用,所以它是个全局函数,不需要封装在类中,直接放在全局作用域下即可。其他代码直接复制。代码太多,这里就不贴了。
然后来到我们的主文件中,新建一个D3D12InitApp类继承自D3D12App类,实例化后直接主函数中调用即可。其实这个文件,除了WinMain函数外就是被复写的Draw函数了。类声明代码如下:
class D3D12InitApp : public D3D12App
{
public:
D3D12InitApp();
~D3D12InitApp();
private:
virtual void Draw()override;
};
主函数代码如下:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, PSTR cmdLine, int nShowCmd)
{
#if defined(DEBUG) | defined(_DEBUG)
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif
try
{
D3D12InitApp theApp;
if ( !theApp.Init(hInstance, nShowCmd) )
return 0;
return theApp.Run();
}
catch (DxException& e)
{
MessageBox(nullptr, e.ToString().c_str(), L"HR Failed", MB_OK);
return 0;
}
}
运行画面和之前一样。
![549c7549d0a1aeed3ee8ccf31cef55e7.png](https://i-blog.csdnimg.cn/blog_migrate/dffa5f1887271715a90f1dfc4d9e4765.png)
至此我们完成了所有的DX12初始化。接下来我们将进入下一篇章《绘制几何体》。