DX12开发中的常用工具类设计
可控生命周期的单例模板
在图形引擎开发中,许多核心系统需要全局唯一实例,但又希望精确控制其创建和销毁时机。以下模板实现了显式初始化的单例模式:
template<typename T>
class Singleton
{
public:
template<typename... Args>
static void Initialize(Args&&... params)
{
if (!s_pInstance)
{
s_pInstance = new T(std::forward<Args>(params)...);
}
}
static void Terminate() noexcept
{
delete s_pInstance;
s_pInstance = nullptr;
}
static T& GetInstance()
{
return *s_pInstance;
}
protected:
Singleton() = default;
~Singleton() = default;
private:
inline static T* s_pInstance = nullptr;
};
使用注意:由于基类无法访问派生类的保护构造函数,非抽象派生类需将基类声明为友元:
class LightSystem : public Singleton<LightSystem>
{
protected:
friend class Singleton<LightSystem>;
LightSystem() = default;
};
ImGui渲染管理框架
基于CRTP(奇异递归模板模式)设计的ImGui管理基类,使单例能够访问派生类的专属接口:
template<typename Derived>
class ImGuiRenderer : public Singleton<Derived>
{
public:
bool Setup(ID3D12Device* pDevice, HWND hwnd,
int swapChainBuffers, DXGI_FORMAT renderFormat)
{
return static_cast<Derived*>(this)->OnSetup(
pDevice, hwnd, swapChainBuffers, renderFormat);
}
void Tick(const FrameTimer& deltaTime)
{
ImGui::NewFrame();
static_cast<Derived*>(this)->OnRenderUI(deltaTime);
ImGui::Render();
}
void Draw(ID3D12GraphicsCommandList* pCmdList)
{
ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), pCmdList);
}
protected:
virtual bool OnSetup(ID3D12Device*, HWND, int, DXGI_FORMAT) = 0;
virtual void OnRenderUI(const FrameTimer&) = 0;
};
光源管理系统
管理场景光源数据,支持多帧资源的CPU/GPU同步:
class LightSystem : public Singleton<LightSystem>
{
public:
LightSystem(ID3D12Device* pDevice, UINT frameBufferCount,
int maxDirectional = 8,
int maxOmni = 16,
int maxProjector = 8);
void SetDirectionalLight(size_t slot, const DirectionalLight& data);
void SetPointLight(size_t slot, const PointLight& data);
void SetSpotLight(size_t slot, const SpotLight& data);
D3D12_GPU_VIRTUAL_ADDRESS GetCurrentFrameAddress() const;
std::vector<D3D_SHADER_MACRO> GenerateShaderDefines(
const char* dirMacro,
const char* pointMacro,
const char* spotMacro) const;
void FlushChanges();
private:
void AllocateLightBuffer(ID3D12Device* pDevice);
ComPtr<ID3D12Resource> m_pLightUploadBuffer;
const UINT m_FrameCount;
UINT m_DirtyFrameCounter = 0;
bool m_bLightsChanged = false;
};
核心机制:m_DirtyFrameCounter用于追踪数据变更,仅在有修改时执行GPU上传,避免每帧拷贝。
几何数据容器
内存中的原始网格数据,支持多种索引格式:
struct RawMesh
{
struct VertexLayout
{
XMFLOAT3 position;
XMFLOAT3 normal;
XMFLOAT4 tangent;
XMFLOAT3 bitangent;
XMFLOAT2 uv;
};
std::vector<VertexLayout> vertices;
std::vector<uint32_t> indices32;
std::vector<uint16_t> indices16;
BoundingBox localBounds;
};
模型导入数据结构
Assimp导入后的中间表示,关联材质与几何:
struct SubMeshInfo
{
std::string identifier;
RawMesh geometryData;
BoundingBox bounds;
UINT materialSlot;
};
struct ModelAsset
{
std::string assetName;
std::unordered_map<std::string, SubMeshInfo> subMeshes;
std::vector<PbrMaterial> materials;
};
GPU就绪的网格资源
上传至显存后的最终渲染数据,包含绘制参数:
struct GpuMesh
{
std::string resourceName;
// CPU端备份(用于动态更新或调试)
ComPtr<ID3DBlob> vertexDataCPU;
ComPtr<ID3lob> indexDataCPU;
// GPU资源
ComPtr<ID3D12Resource> vertexBuffer;
ComPtr<ID3D12Resource> indexBuffer;
ComPtr<ID3D12Resource> vertexUploader; // 可释放
ComPtr<ID3D12Resource> indexUploader; // 可释放
// 视图参数
UINT vertexStride = 0;
UINT vertexBufferSize = 0;
DXGI_FORMAT indexFormat = DXGI_FORMAT_R16_UINT;
UINT indexBufferSize = 0;
// 子网格绘制参数(支持多部分模型)
struct DrawCommand
{
UINT indexCount;
UINT startIndex;
INT baseVertex;
};
std::unordered_map<std::string, DrawCommand> drawCommands;
D3D12_VERTEX_BUFFER_VIEW GetVertexView() const
{
return { vertexBuffer->GetGPUVirtualAddress(),
vertexBufferSize, vertexStride };
}
D3D12_INDEX_BUFFER_VIEW GetIndexView() const
{
return { indexBuffer->GetGPUVirtualAddress(),
indexBufferSize, indexFormat };
}
void ReleaseUploaders()
{
vertexUploader.Reset();
indexUploader.Reset();
}
};