- D3D12Shader now implements IShader interface with GetBytecode, GetBytecodeSize, GetType, GetInputLayout - D3D12SwapChain now implements ISwapChain interface with GetBackBuffer returning IResource* - Added D3D12Texture back buffer storage to SwapChain - Fixed ISwapChain const correctness (GetCurrentBackBufferIndex, GetBackBuffer) - main.cpp: use GetD3D12Bytecode() instead of GetBytecode() for PSO creation
718 lines
29 KiB
C++
718 lines
29 KiB
C++
#include <windows.h>
|
|
#include <d3d12.h>
|
|
#include <dxgi1_4.h>
|
|
#include "XCEngine/RHI/D3D12/D3D12Shader.h"
|
|
#include <DirectXMath.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
#include <stdarg.h>
|
|
#include <algorithm>
|
|
#include <unordered_map>
|
|
#include <string>
|
|
#include <cstring>
|
|
#include "stbi/stb_image.h"
|
|
#include "XCEngine/RHI/Enums.h"
|
|
#include "XCEngine/RHI/Types.h"
|
|
#include "XCEngine/RHI/RHIDevice.h"
|
|
#include "XCEngine/RHI/D3D12/D3D12Enum.h"
|
|
#include "XCEngine/RHI/D3D12/D3D12Device.h"
|
|
#include "XCEngine/RHI/D3D12/D3D12CommandQueue.h"
|
|
#include "XCEngine/RHI/D3D12/D3D12CommandAllocator.h"
|
|
#include "XCEngine/RHI/D3D12/D3D12CommandList.h"
|
|
#include "XCEngine/RHI/D3D12/D3D12DescriptorHeap.h"
|
|
#include "XCEngine/RHI/D3D12/D3D12Fence.h"
|
|
#include "XCEngine/RHI/D3D12/D3D12SwapChain.h"
|
|
#include "XCEngine/RHI/D3D12/D3D12RootSignature.h"
|
|
#include "XCEngine/RHI/D3D12/D3D12PipelineState.h"
|
|
#include "XCEngine/RHI/D3D12/D3D12Buffer.h"
|
|
#include "XCEngine/RHI/D3D12/D3D12Texture.h"
|
|
#include "XCEngine/RHI/D3D12/D3D12RenderTargetView.h"
|
|
#include "XCEngine/RHI/D3D12/D3D12DepthStencilView.h"
|
|
#include "XCEngine/RHI/D3D12/D3D12ShaderResourceView.h"
|
|
#include "XCEngine/RHI/D3D12/D3D12Screenshot.h"
|
|
#include "XCEngine/Debug/Logger.h"
|
|
#include "XCEngine/Debug/ConsoleLogSink.h"
|
|
#include "XCEngine/Debug/FileLogSink.h"
|
|
|
|
using namespace XCEngine::RHI;
|
|
using namespace XCEngine::Debug;
|
|
|
|
#pragma comment(lib,"d3d12.lib")
|
|
#pragma comment(lib,"dxgi.lib")
|
|
#pragma comment(lib,"d3dcompiler.lib")
|
|
#pragma comment(lib,"winmm.lib")
|
|
|
|
void Log(const char* format, ...) {
|
|
char buffer[1024];
|
|
va_list args;
|
|
va_start(args, format);
|
|
vsnprintf(buffer, sizeof(buffer), format, args);
|
|
va_end(args);
|
|
|
|
Logger::Get().Debug(LogCategory::Rendering, buffer);
|
|
}
|
|
|
|
//=================================================================================
|
|
// D3D12 核心全局对象 (最小渲染所需)
|
|
//=================================================================================
|
|
XCEngine::RHI::IRHIDevice* gDevice = nullptr;
|
|
XCEngine::RHI::D3D12Device gD3D12Device; // 底层实现
|
|
XCEngine::RHI::D3D12CommandQueue gCommandQueue;
|
|
XCEngine::RHI::D3D12SwapChain gSwapChain;
|
|
|
|
// 渲染目标 (SwapChain的后台Buffer)
|
|
XCEngine::RHI::D3D12Texture gColorRTs[2]; // 颜色缓冲 (双缓冲)
|
|
int gCurrentRTIndex = 0;
|
|
|
|
// 描述符堆
|
|
XCEngine::RHI::D3D12DescriptorHeap gSwapChainRTVHeap; // RTV堆
|
|
XCEngine::RHI::D3D12DescriptorHeap gSwapChainDSVHeap; // DSV堆
|
|
XCEngine::RHI::D3D12RenderTargetView gSwapChainRTVs[2];
|
|
XCEngine::RHI::D3D12DepthStencilView gSwapChainDSV;
|
|
UINT gRTVDescriptorSize = 0;
|
|
UINT gDSVDescriptorSize = 0;
|
|
|
|
// 命令相关
|
|
XCEngine::RHI::D3D12CommandAllocator gCommandAllocator;
|
|
XCEngine::RHI::D3D12CommandList gCommandList;
|
|
XCEngine::RHI::D3D12RootSignature gRootSignature;
|
|
XCEngine::RHI::D3D12PipelineState gPipelineState;
|
|
XCEngine::RHI::D3D12Shader gVertexShader;
|
|
XCEngine::RHI::D3D12Shader gGeometryShader;
|
|
XCEngine::RHI::D3D12Shader gPixelShader;
|
|
|
|
// Buffer objects
|
|
XCEngine::RHI::D3D12Buffer gConstantBuffer; // matrices
|
|
XCEngine::RHI::D3D12Buffer gMaterialBuffer; // material data
|
|
XCEngine::RHI::D3D12Texture gTexture; // earth texture
|
|
XCEngine::RHI::D3D12Texture gDepthStencil; // depth stencil buffer
|
|
XCEngine::RHI::D3D12ShaderResourceView gTextureSRV; // texture SRV
|
|
|
|
// 同步对象
|
|
XCEngine::RHI::D3D12Fence gFence;
|
|
UINT64 gFenceValue = 0;
|
|
|
|
//=================================================================================
|
|
// 工具函数
|
|
//=================================================================================
|
|
float srandom() {
|
|
float number = float(rand()) / float(RAND_MAX);
|
|
number *= 2.0f;
|
|
number -= 1.0f;
|
|
return number;
|
|
}
|
|
|
|
//=================================================================================
|
|
// 数据结构定义
|
|
//=================================================================================
|
|
struct StaticMeshComponentVertexData {
|
|
float mPosition[4];
|
|
float mTexcoord[4];
|
|
float mNormal[4];
|
|
float mTangent[4];
|
|
};
|
|
|
|
struct SubMesh {
|
|
XCEngine::RHI::D3D12Buffer mIBO;
|
|
D3D12_INDEX_BUFFER_VIEW mIBView;
|
|
int mIndexCount;
|
|
};
|
|
|
|
//=================================================================================
|
|
// 网格组件类 (StaticMeshComponent)
|
|
// 封装顶点缓冲(VBO)、索引缓冲(IBO)和渲染逻辑
|
|
//=================================================================================
|
|
class StaticMeshComponent {
|
|
public:
|
|
XCEngine::RHI::D3D12Buffer mVBO;
|
|
D3D12_VERTEX_BUFFER_VIEW mVBOView;
|
|
StaticMeshComponentVertexData* mVertexData;
|
|
int mVertexCount;
|
|
std::unordered_map<std::string, SubMesh*> mSubMeshes;
|
|
|
|
void SetVertexCount(int inVertexCount) {
|
|
mVertexCount = inVertexCount;
|
|
mVertexData = new StaticMeshComponentVertexData[inVertexCount];
|
|
memset(mVertexData, 0, sizeof(StaticMeshComponentVertexData) * inVertexCount);
|
|
}
|
|
void SetVertexPosition(int inIndex, float inX, float inY, float inZ, float inW = 1.0f) {
|
|
mVertexData[inIndex].mPosition[0] = inX;
|
|
mVertexData[inIndex].mPosition[1] = inY;
|
|
mVertexData[inIndex].mPosition[2] = inZ;
|
|
mVertexData[inIndex].mPosition[3] = inW;
|
|
}
|
|
void SetVertexTexcoord(int inIndex, float inX, float inY, float inZ, float inW = 1.0f) {
|
|
mVertexData[inIndex].mTexcoord[0] = inX;
|
|
mVertexData[inIndex].mTexcoord[1] = inY;
|
|
mVertexData[inIndex].mTexcoord[2] = inZ;
|
|
mVertexData[inIndex].mTexcoord[3] = inW;
|
|
}
|
|
void SetVertexNormal(int inIndex, float inX, float inY, float inZ, float inW = 1.0f) {
|
|
mVertexData[inIndex].mNormal[0] = inX;
|
|
mVertexData[inIndex].mNormal[1] = inY;
|
|
mVertexData[inIndex].mNormal[2] = inZ;
|
|
mVertexData[inIndex].mNormal[3] = inW;
|
|
}
|
|
void SetVertexTangent(int inIndex, float inX, float inY, float inZ, float inW = 1.0f) {
|
|
mVertexData[inIndex].mTangent[0] = inX;
|
|
mVertexData[inIndex].mTangent[1] = inY;
|
|
mVertexData[inIndex].mTangent[2] = inZ;
|
|
mVertexData[inIndex].mTangent[3] = inW;
|
|
}
|
|
void InitFromFile(ID3D12GraphicsCommandList* inCommandList, const char* inFilePath) {
|
|
FILE* pFile = nullptr;
|
|
errno_t err = fopen_s(&pFile, inFilePath, "rb");
|
|
if (err == 0) {
|
|
int temp = 0;
|
|
fread(&temp, 4, 1, pFile);
|
|
mVertexCount = temp;
|
|
mVertexData = new StaticMeshComponentVertexData[mVertexCount];
|
|
fread(mVertexData, 1, sizeof(StaticMeshComponentVertexData) * mVertexCount, pFile);
|
|
mVBO.InitializeWithData(gD3D12Device.GetDevice(), inCommandList, mVertexData,
|
|
sizeof(StaticMeshComponentVertexData) * mVertexCount,
|
|
D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER);
|
|
mVBOView.BufferLocation = mVBO.GetGPUVirtualAddress();
|
|
mVBOView.SizeInBytes = sizeof(StaticMeshComponentVertexData) * mVertexCount;
|
|
mVBOView.StrideInBytes = sizeof(StaticMeshComponentVertexData);
|
|
|
|
while (!feof(pFile)) {
|
|
fread(&temp, 4, 1, pFile);
|
|
if (feof(pFile)) {
|
|
break;
|
|
}
|
|
char name[256] = { 0 };
|
|
fread(name, 1, temp, pFile);
|
|
fread(&temp, 4, 1, pFile);
|
|
SubMesh* submesh = new SubMesh;
|
|
submesh->mIndexCount = temp;
|
|
unsigned int* indexes = new unsigned int[temp];
|
|
fread(indexes, 1, sizeof(unsigned int) * temp, pFile);
|
|
submesh->mIBO.InitializeWithData(gD3D12Device.GetDevice(), inCommandList, indexes,
|
|
sizeof(unsigned int) * temp,
|
|
D3D12_RESOURCE_STATE_INDEX_BUFFER);
|
|
|
|
submesh->mIBView.BufferLocation = submesh->mIBO.GetGPUVirtualAddress();
|
|
submesh->mIBView.SizeInBytes = sizeof(unsigned int) * temp;
|
|
submesh->mIBView.Format = DXGI_FORMAT_R32_UINT;
|
|
mSubMeshes.insert(std::pair<std::string, SubMesh*>(name, submesh));
|
|
delete[] indexes;
|
|
}
|
|
fclose(pFile);
|
|
}
|
|
}
|
|
void Render(XCEngine::RHI::D3D12CommandList& inCommandList) {
|
|
D3D12_VERTEX_BUFFER_VIEW vbos[] = {
|
|
mVBOView
|
|
};
|
|
inCommandList.SetVertexBuffers(0, 1, vbos);
|
|
if (mSubMeshes.empty()) {
|
|
inCommandList.Draw(mVertexCount, 1, 0, 0);
|
|
}
|
|
else {
|
|
for (auto iter = mSubMeshes.begin();
|
|
iter != mSubMeshes.end(); iter++) {
|
|
inCommandList.SetIndexBuffer(iter->second->mIBO.GetResource(), 0, XCEngine::RHI::Format::R32_UInt);
|
|
inCommandList.DrawIndexed(iter->second->mIndexCount, 1, 0, 0, 0);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
//=================================================================================
|
|
// 根签名初始化 (RootSignature)
|
|
// 定义GPU资源绑定规则: CBV(常量缓冲) / SRV(着色器资源) / DescriptorTable
|
|
//=================================================================================
|
|
ID3D12RootSignature* InitRootSignature() {
|
|
D3D12_ROOT_PARAMETER rootParameters[4];
|
|
rootParameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
|
|
rootParameters[1].ShaderVisibility = ToD3D12(ShaderVisibility::Vertex);
|
|
rootParameters[1].Constants.RegisterSpace = 0;
|
|
rootParameters[1].Constants.ShaderRegister = 0;
|
|
rootParameters[1].Constants.Num32BitValues = 4;
|
|
|
|
rootParameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
|
|
rootParameters[0].ShaderVisibility = ToD3D12(ShaderVisibility::All);
|
|
rootParameters[0].Descriptor.RegisterSpace = 0;
|
|
rootParameters[0].Descriptor.ShaderRegister = 1;
|
|
|
|
D3D12_DESCRIPTOR_RANGE descriptorRange[1];
|
|
descriptorRange[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
|
|
descriptorRange[0].RegisterSpace = 0;
|
|
descriptorRange[0].BaseShaderRegister = 0;
|
|
descriptorRange[0].NumDescriptors = 1;
|
|
descriptorRange[0].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
|
|
|
rootParameters[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
|
|
rootParameters[2].ShaderVisibility = ToD3D12(ShaderVisibility::Pixel);
|
|
rootParameters[2].DescriptorTable.pDescriptorRanges = descriptorRange;
|
|
rootParameters[2].DescriptorTable.NumDescriptorRanges = _countof(descriptorRange);
|
|
|
|
rootParameters[3].ParameterType = D3D12_ROOT_PARAMETER_TYPE_SRV;
|
|
rootParameters[3].ShaderVisibility = ToD3D12(ShaderVisibility::All);
|
|
rootParameters[3].Descriptor.RegisterSpace = 1;
|
|
rootParameters[3].Descriptor.ShaderRegister = 0;
|
|
|
|
D3D12_STATIC_SAMPLER_DESC samplerDesc[1];
|
|
memset(samplerDesc, 0, sizeof(D3D12_STATIC_SAMPLER_DESC) * _countof(samplerDesc));
|
|
samplerDesc[0].Filter = ToD3D12(FilterMode::Linear);
|
|
samplerDesc[0].AddressU = ToD3D12(TextureAddressMode::Clamp);
|
|
samplerDesc[0].AddressV = ToD3D12(TextureAddressMode::Clamp);
|
|
samplerDesc[0].AddressW = ToD3D12(TextureAddressMode::Clamp);
|
|
samplerDesc[0].BorderColor = ToD3D12(BorderColor::OpaqueBlack);
|
|
samplerDesc[0].MaxLOD = D3D12_FLOAT32_MAX;
|
|
samplerDesc[0].RegisterSpace = 0;
|
|
samplerDesc[0].ShaderRegister = 0;
|
|
samplerDesc[0].ShaderVisibility = ToD3D12(ShaderVisibility::Pixel);
|
|
|
|
D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc = {};
|
|
rootSignatureDesc.NumParameters = _countof(rootParameters);
|
|
rootSignatureDesc.pParameters = rootParameters;
|
|
rootSignatureDesc.NumStaticSamplers = _countof(samplerDesc);
|
|
rootSignatureDesc.pStaticSamplers = samplerDesc;
|
|
rootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
|
|
|
|
gRootSignature.Initialize(gD3D12Device.GetDevice(), rootSignatureDesc);
|
|
|
|
return gRootSignature.GetRootSignature();
|
|
}
|
|
|
|
//=================================================================================
|
|
//=================================================================================
|
|
//=================================================================================
|
|
// 渲染管线状态对象 (PSO)
|
|
// 包含: InputLayout / VS/GS/PS / Rasterizer / DepthStencil / Blend
|
|
//=================================================================================
|
|
ID3D12PipelineState* CreatePSO(ID3D12RootSignature* inID3D12RootSignature,
|
|
D3D12_SHADER_BYTECODE inVertexShader, D3D12_SHADER_BYTECODE inPixelShader,
|
|
D3D12_SHADER_BYTECODE inGSShader) {
|
|
D3D12_INPUT_ELEMENT_DESC vertexDataElementDesc[] = {
|
|
{"POSITION",0,DXGI_FORMAT_R32G32B32A32_FLOAT,0,0,D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,0},
|
|
{"TEXCOORD",0,DXGI_FORMAT_R32G32B32A32_FLOAT,0,sizeof(float) * 4,D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,0},
|
|
{"NORMAL",0,DXGI_FORMAT_R32G32B32A32_FLOAT,0,sizeof(float) * 8,D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,0},
|
|
{"TANGENT",0,DXGI_FORMAT_R32G32B32A32_FLOAT,0,sizeof(float) * 12,D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,0}
|
|
};
|
|
D3D12_INPUT_LAYOUT_DESC vertexDataLayoutDesc = {};
|
|
vertexDataLayoutDesc.NumElements = 4;
|
|
vertexDataLayoutDesc.pInputElementDescs = vertexDataElementDesc;
|
|
|
|
D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
|
|
psoDesc.pRootSignature = inID3D12RootSignature;
|
|
psoDesc.VS = inVertexShader;
|
|
psoDesc.GS = inGSShader;
|
|
psoDesc.PS = inPixelShader;
|
|
psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
psoDesc.DSVFormat = DXGI_FORMAT_D24_UNORM_S8_UINT;
|
|
psoDesc.SampleDesc.Count = 1;
|
|
psoDesc.SampleDesc.Quality = 0;
|
|
psoDesc.SampleMask = 0xffffffff;
|
|
psoDesc.InputLayout = vertexDataLayoutDesc;
|
|
psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
|
|
|
|
psoDesc.RasterizerState.FillMode = ToD3D12(FillMode::Solid);
|
|
psoDesc.RasterizerState.CullMode = ToD3D12(CullMode::Back);
|
|
psoDesc.RasterizerState.DepthClipEnable = TRUE;
|
|
|
|
psoDesc.DepthStencilState.DepthEnable = true;
|
|
psoDesc.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
|
|
psoDesc.DepthStencilState.DepthFunc = ToD3D12(ComparisonFunc::LessEqual);
|
|
|
|
psoDesc.BlendState = { 0 };
|
|
D3D12_RENDER_TARGET_BLEND_DESC rtBlendDesc = {
|
|
FALSE,FALSE,
|
|
ToD3D12(BlendFactor::SrcAlpha),ToD3D12(BlendFactor::InvSrcAlpha),ToD3D12(BlendOp::Add),
|
|
ToD3D12(BlendFactor::SrcAlpha),ToD3D12(BlendFactor::InvSrcAlpha),ToD3D12(BlendOp::Add),
|
|
ToD3D12(LogicOp::Noop),
|
|
static_cast<UINT8>(ColorWriteMask::All),
|
|
};
|
|
for (int i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i)
|
|
psoDesc.BlendState.RenderTarget[i] = rtBlendDesc;
|
|
psoDesc.NumRenderTargets = 1;
|
|
ID3D12PipelineState* d3d12PSO = nullptr;
|
|
|
|
gPipelineState.Initialize(gD3D12Device.GetDevice(), psoDesc);
|
|
if (!gPipelineState.GetPipelineState()) {
|
|
return nullptr;
|
|
}
|
|
return gPipelineState.GetPipelineState();
|
|
}
|
|
|
|
//=================================================================================
|
|
// D3D12 初始化核心函数 (InitD3D12)
|
|
// 最小渲染系统初始化流程:
|
|
// 1. 启用Debug层 (可选, _DEBUG)
|
|
// 2. 创建IDXGIFactory4
|
|
// 3. 枚举Adapter, 创建ID3D12Device
|
|
// 4. 创建CommandQueue (命令队列)
|
|
// 5. 创建SwapChain (交换链)
|
|
// 6. 创建DepthStencilBuffer (深度缓冲)
|
|
// 7. 创建RTV/DSV描述符堆
|
|
// 8. 创建RenderTargetView / DepthStencilView
|
|
// 9. 创建CommandAllocator / CommandList
|
|
// 10. 创建Fence (同步)
|
|
//=================================================================================
|
|
bool InitD3D12(HWND inHWND, int inWidth, int inHeight) {
|
|
if (!gD3D12Device.Initialize()) {
|
|
return false;
|
|
}
|
|
|
|
// 获取抽象接口指针
|
|
gDevice = &gD3D12Device;
|
|
|
|
ID3D12Device* device = gD3D12Device.GetDevice();
|
|
IDXGIFactory4* dxgiFactory = gD3D12Device.GetFactory();
|
|
|
|
if (!gCommandQueue.Initialize(device, XCEngine::RHI::CommandQueueType::Direct)) {
|
|
return false;
|
|
}
|
|
|
|
DXGI_SWAP_CHAIN_DESC swapChainDesc = {};
|
|
swapChainDesc.BufferCount = 2;
|
|
swapChainDesc.BufferDesc = {};
|
|
swapChainDesc.BufferDesc.Width = inWidth;
|
|
swapChainDesc.BufferDesc.Height = inHeight;
|
|
swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
|
swapChainDesc.OutputWindow = inHWND;
|
|
swapChainDesc.SampleDesc.Count = 1;
|
|
swapChainDesc.Windowed = true;
|
|
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
|
|
|
|
IDXGISwapChain* swapChain = nullptr;
|
|
dxgiFactory->CreateSwapChain(gCommandQueue.GetCommandQueue(), &swapChainDesc, &swapChain);
|
|
gSwapChain.Initialize(swapChain, inWidth, inHeight);
|
|
|
|
gDepthStencil.InitializeDepthStencil(device, inWidth, inHeight);
|
|
|
|
D3D12_DESCRIPTOR_HEAP_DESC d3dDescriptorHeapDescRTV = {};
|
|
d3dDescriptorHeapDescRTV.NumDescriptors = 2;
|
|
d3dDescriptorHeapDescRTV.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
|
|
gSwapChainRTVHeap.Initialize(device, XCEngine::RHI::DescriptorHeapType::RTV, 2);
|
|
gRTVDescriptorSize = gD3D12Device.GetDescriptorHandleIncrementSize(XCEngine::RHI::DescriptorHeapType::RTV);
|
|
|
|
D3D12_DESCRIPTOR_HEAP_DESC d3dDescriptorHeapDescDSV = {};
|
|
d3dDescriptorHeapDescDSV.NumDescriptors = 1;
|
|
d3dDescriptorHeapDescDSV.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
|
|
gSwapChainDSVHeap.Initialize(device, XCEngine::RHI::DescriptorHeapType::DSV, 1);
|
|
gDSVDescriptorSize = gD3D12Device.GetDescriptorHandleIncrementSize(XCEngine::RHI::DescriptorHeapType::DSV);
|
|
|
|
D3D12_CPU_DESCRIPTOR_HANDLE rtvHeapStart = gSwapChainRTVHeap.GetCPUDescriptorHandleForHeapStart();
|
|
for (int i = 0; i < 2; i++) {
|
|
ID3D12Resource* buffer = nullptr;
|
|
gSwapChain.GetSwapChain()->GetBuffer(i, IID_PPV_ARGS(&buffer));
|
|
gColorRTs[i].InitializeFromExisting(buffer);
|
|
D3D12_CPU_DESCRIPTOR_HANDLE rtvPointer;
|
|
rtvPointer.ptr = rtvHeapStart.ptr + i * gRTVDescriptorSize;
|
|
gSwapChainRTVs[i].InitializeAt(device, gColorRTs[i].GetResource(), rtvPointer, nullptr);
|
|
}
|
|
D3D12_DEPTH_STENCIL_VIEW_DESC d3dDSViewDesc = {};
|
|
d3dDSViewDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
|
|
d3dDSViewDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
|
|
|
|
gSwapChainDSV.InitializeAt(device, gDepthStencil.GetResource(), gSwapChainDSVHeap.GetCPUDescriptorHandleForHeapStart(), &d3dDSViewDesc);
|
|
|
|
gCommandAllocator.Initialize(device, XCEngine::RHI::CommandQueueType::Direct);
|
|
gCommandList.Initialize(device, XCEngine::RHI::CommandQueueType::Direct, gCommandAllocator.GetCommandAllocator());
|
|
|
|
gFence.Initialize(device, 0);
|
|
|
|
return true;
|
|
}
|
|
|
|
//=================================================================================
|
|
// 命令相关辅助函数
|
|
//=================================================================================
|
|
void WaitForCompletionOfCommandList() {
|
|
UINT64 completed = gFence.GetCompletedValue();
|
|
UINT64 current = gFenceValue;
|
|
Log("[DEBUG] WaitForCompletion: completed=%llu, waiting for=%llu\n", completed, current);
|
|
if (completed < current) {
|
|
Log("[DEBUG] WaitForCompletion: waiting...\n");
|
|
gFence.Wait(current);
|
|
Log("[DEBUG] WaitForCompletion: done\n");
|
|
}
|
|
}
|
|
|
|
//=================================================================================
|
|
// 命令列表结束提交
|
|
// 关闭CommandList → ExecuteCommandLists → Signal Fence
|
|
//=================================================================================
|
|
void EndCommandList() {
|
|
gCommandList.Close();
|
|
ID3D12CommandList* ppCommandLists[] = { gCommandList.GetCommandList() };
|
|
gCommandQueue.ExecuteCommandLists(1, ppCommandLists);
|
|
gFenceValue += 1;
|
|
gCommandQueue.Signal(gFence.GetFence(), gFenceValue);
|
|
}
|
|
|
|
//=================================================================================
|
|
// 开始渲染到SwapChain
|
|
// 1. 获取当前BackBuffer索引
|
|
// 2. 状态转换: PRESENT → RENDER_TARGET
|
|
// 3. 设置RenderTargets (Color + Depth)
|
|
// 4. 设置Viewport/Scissor
|
|
// 5. Clear Color/Depth
|
|
//=================================================================================
|
|
void BeginRenderToSwapChain(XCEngine::RHI::D3D12CommandList& inCommandList) {
|
|
gCurrentRTIndex = gSwapChain.GetCurrentBackBufferIndex();
|
|
inCommandList.TransitionBarrier(gColorRTs[gCurrentRTIndex].GetResource(), XCEngine::RHI::ResourceStates::Present, XCEngine::RHI::ResourceStates::RenderTarget);
|
|
D3D12_CPU_DESCRIPTOR_HANDLE colorRT, dsv;
|
|
dsv.ptr = gSwapChainDSVHeap.GetCPUDescriptorHandleForHeapStart().ptr;
|
|
colorRT.ptr = gSwapChainRTVHeap.GetCPUDescriptorHandleForHeapStart().ptr + gCurrentRTIndex * gRTVDescriptorSize;
|
|
inCommandList.SetRenderTargets(1, &colorRT, &dsv);
|
|
XCEngine::RHI::Viewport viewport = { 0.0f, 0.0f, 1280.0f, 720.0f, 0.0f, 1.0f };
|
|
XCEngine::RHI::Rect scissorRect = { 0, 0, 1280, 720 };
|
|
inCommandList.SetViewport(viewport);
|
|
inCommandList.SetScissorRect(scissorRect);
|
|
const float clearColor[] = { 0.0f,0.0f,0.0f,1.0f };
|
|
inCommandList.ClearRenderTargetView(colorRT, clearColor, 0, nullptr);
|
|
inCommandList.ClearDepthStencilView(dsv, D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 1.0f, 0, 0, nullptr);
|
|
}
|
|
|
|
//=================================================================================
|
|
// 结束渲染到SwapChain
|
|
// 状态转换: RENDER_TARGET → PRESENT
|
|
//=================================================================================
|
|
void EndRenderToSwapChain(XCEngine::RHI::D3D12CommandList& inCommandList) {
|
|
inCommandList.TransitionBarrier(gColorRTs[gCurrentRTIndex].GetResource(), XCEngine::RHI::ResourceStates::RenderTarget, XCEngine::RHI::ResourceStates::Present);
|
|
}
|
|
|
|
//=================================================================================
|
|
// 截图保存 Debug 工具
|
|
// 使用 RHI 模块的 D3D12Screenshot 类
|
|
//=================================================================================
|
|
bool SaveScreenshot(const char* filename, int width, int height) {
|
|
Log("[DEBUG] SaveScreenshot: start\n");
|
|
|
|
ID3D12Device* device = gD3D12Device.GetDevice();
|
|
ID3D12CommandQueue* queue = gCommandQueue.GetCommandQueue();
|
|
ID3D12Resource* renderTarget = gColorRTs[gCurrentRTIndex].GetResource();
|
|
|
|
Log("[DEBUG] SaveScreenshot: calling D3D12Screenshot::Capture\n");
|
|
bool result = XCEngine::RHI::D3D12Screenshot::Capture(
|
|
device, queue, renderTarget, filename, (uint32_t)width, (uint32_t)height);
|
|
|
|
Log("[DEBUG] SaveScreenshot: done, result=%d\n", result);
|
|
return result;
|
|
}
|
|
|
|
//=================================================================================
|
|
// Win32 窗口相关
|
|
//=================================================================================
|
|
LPCTSTR gWindowClassName = L"BattleFire";
|
|
|
|
//=================================================================================
|
|
// 窗口消息回调函数
|
|
//=================================================================================
|
|
LRESULT CALLBACK WindowProc(HWND inHWND, UINT inMSG, WPARAM inWParam, LPARAM inLParam) {
|
|
switch (inMSG) {
|
|
case WM_CLOSE:
|
|
PostQuitMessage(0);
|
|
break;
|
|
}
|
|
return DefWindowProc(inHWND, inMSG, inWParam, inLParam);
|
|
}
|
|
|
|
//=================================================================================
|
|
// 主入口函数 WinMain
|
|
// 程序入口点
|
|
//=================================================================================
|
|
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int inShowCmd) {
|
|
Logger::Get().AddSink(std::make_unique<FileLogSink>("D3D12_engine_log.txt"));
|
|
Logger::Get().SetMinimumLevel(LogLevel::Debug);
|
|
|
|
AllocConsole();
|
|
freopen("CONOUT$", "w", stdout);
|
|
Log("[DEBUG] D3D12 Test Application Started\n");
|
|
|
|
WNDCLASSEX wndClassEx;
|
|
wndClassEx.cbSize = sizeof(WNDCLASSEX);
|
|
wndClassEx.style = CS_HREDRAW | CS_VREDRAW;
|
|
wndClassEx.cbClsExtra = NULL;
|
|
wndClassEx.cbWndExtra = NULL;
|
|
wndClassEx.hInstance = hInstance;
|
|
wndClassEx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
|
|
wndClassEx.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
|
|
wndClassEx.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wndClassEx.hbrBackground = NULL;
|
|
wndClassEx.lpszMenuName = NULL;
|
|
wndClassEx.lpszClassName = gWindowClassName;
|
|
wndClassEx.lpfnWndProc = WindowProc;
|
|
if (!RegisterClassEx(&wndClassEx)) {
|
|
MessageBox(NULL, L"Register Class Failed!", L"Error", MB_OK | MB_ICONERROR);
|
|
return -1;
|
|
}
|
|
|
|
int viewportWidth = 1280;
|
|
int viewportHeight = 720;
|
|
RECT rect;
|
|
rect.left = 0;
|
|
rect.top = 0;
|
|
rect.right = viewportWidth;
|
|
rect.bottom = viewportHeight;
|
|
AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);
|
|
int windowWidth = rect.right - rect.left;
|
|
int windowHeight = rect.bottom - rect.top;
|
|
HWND hwnd = CreateWindowEx(NULL,
|
|
gWindowClassName,
|
|
L"My Render Window",
|
|
WS_OVERLAPPEDWINDOW,
|
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
|
windowWidth, windowHeight,
|
|
NULL,
|
|
NULL,
|
|
hInstance,
|
|
NULL);
|
|
if (!hwnd) {
|
|
MessageBox(NULL, L"Create Window Failed!", L"Error", MB_OK | MB_ICONERROR);
|
|
return -1;
|
|
}
|
|
|
|
InitD3D12(hwnd, 1280, 720);
|
|
ID3D12CommandAllocator* commandAllocator = gCommandAllocator.GetCommandAllocator();
|
|
StaticMeshComponent staticMeshComponent;
|
|
staticMeshComponent.InitFromFile(gCommandList.GetCommandList(), "Res/Model/Sphere.lhsm");
|
|
|
|
ID3D12RootSignature* rootSignature = InitRootSignature();
|
|
gVertexShader.CompileFromFile(L"Res/Shader/gs.hlsl", "MainVS", "vs_5_1");
|
|
gGeometryShader.CompileFromFile(L"Res/Shader/gs.hlsl", "MainGS", "gs_5_1");
|
|
gPixelShader.CompileFromFile(L"Res/Shader/gs.hlsl", "MainPS", "ps_5_1");
|
|
ID3D12PipelineState* pso = CreatePSO(rootSignature, gVertexShader.GetD3D12Bytecode(), gPixelShader.GetD3D12Bytecode(), gGeometryShader.GetD3D12Bytecode());
|
|
|
|
gConstantBuffer.Initialize(gD3D12Device.GetDevice(), 65536, D3D12_RESOURCE_STATE_GENERIC_READ, D3D12_HEAP_TYPE_UPLOAD);
|
|
DirectX::XMMATRIX projectionMatrix = DirectX::XMMatrixPerspectiveFovLH(
|
|
(45.0f * 3.141592f) / 180.0f, 1280.0f / 720.0f, 0.1f, 1000.0f);
|
|
DirectX::XMMATRIX viewMatrix = DirectX::XMMatrixIdentity();
|
|
DirectX::XMMATRIX modelMatrix = DirectX::XMMatrixTranslation(0.0f, 0.0f, 5.0f);
|
|
DirectX::XMFLOAT4X4 tempMatrix;
|
|
float matrices[64];
|
|
|
|
DirectX::XMStoreFloat4x4(&tempMatrix, projectionMatrix);
|
|
memcpy(matrices, &tempMatrix, sizeof(float) * 16);
|
|
DirectX::XMStoreFloat4x4(&tempMatrix, viewMatrix);
|
|
memcpy(matrices + 16, &tempMatrix, sizeof(float) * 16);
|
|
DirectX::XMStoreFloat4x4(&tempMatrix, modelMatrix);
|
|
memcpy(matrices + 32, &tempMatrix, sizeof(float) * 16);
|
|
DirectX::XMVECTOR determinant;
|
|
DirectX::XMMATRIX inverseModelMatrix = DirectX::XMMatrixInverse(&determinant, modelMatrix);
|
|
if (DirectX::XMVectorGetX(determinant) != 0.0f) {
|
|
DirectX::XMMATRIX normalMatrix = DirectX::XMMatrixTranspose(inverseModelMatrix);
|
|
DirectX::XMStoreFloat4x4(&tempMatrix, modelMatrix);
|
|
memcpy(matrices + 48, &tempMatrix, sizeof(float) * 16);
|
|
}
|
|
{
|
|
D3D12_RANGE d3d12Range = { 0 };
|
|
unsigned char* pBuffer = nullptr;
|
|
gConstantBuffer.GetResource()->Map(0, &d3d12Range, (void**)&pBuffer);
|
|
memcpy(pBuffer, matrices, sizeof(float) * 64);
|
|
gConstantBuffer.GetResource()->Unmap(0, nullptr);
|
|
}
|
|
|
|
gMaterialBuffer.Initialize(gD3D12Device.GetDevice(), 65536, D3D12_RESOURCE_STATE_GENERIC_READ, D3D12_HEAP_TYPE_UPLOAD);
|
|
struct MaterialData {
|
|
float r;
|
|
};
|
|
MaterialData* materialDatas = new MaterialData[3000];
|
|
for (int i = 0; i < 3000; i++) {
|
|
materialDatas[i].r = srandom() * 0.1f + 0.1f;
|
|
}
|
|
{
|
|
D3D12_RANGE d3d12Range = { 0 };
|
|
unsigned char* pBuffer = nullptr;
|
|
gMaterialBuffer.GetResource()->Map(0, &d3d12Range, (void**)&pBuffer);
|
|
memcpy(pBuffer, materialDatas, sizeof(MaterialData) * 3000);
|
|
gMaterialBuffer.GetResource()->Unmap(0, nullptr);
|
|
}
|
|
|
|
int imageWidth, imageHeight, imageChannel;
|
|
stbi_uc* pixels = stbi_load("Res/Image/earth_d.jpg", &imageWidth, &imageHeight, &imageChannel, 4);
|
|
Log("[DEBUG] Texture loaded: width=%d, height=%d, channels=%d, pixels=%p\n", imageWidth, imageHeight, imageChannel, pixels);
|
|
gTexture.InitializeFromData(gD3D12Device.GetDevice(), gCommandList.GetCommandList(), pixels,
|
|
imageWidth, imageHeight, DXGI_FORMAT_R8G8B8A8_UNORM);
|
|
ID3D12Resource* texture = gTexture.GetResource();
|
|
delete[] pixels;
|
|
ID3D12Device* d3dDevice = gD3D12Device.GetDevice();
|
|
|
|
XCEngine::RHI::D3D12DescriptorHeap srvHeap;
|
|
srvHeap.Initialize(d3dDevice, XCEngine::RHI::DescriptorHeapType::CBV_SRV_UAV, 3, true);
|
|
|
|
ID3D12DescriptorHeap* descriptorHeaps[] = { srvHeap.GetDescriptorHeap() };
|
|
|
|
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
|
|
srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
|
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
|
|
srvDesc.Texture2D.MipLevels = 1;
|
|
|
|
D3D12_CPU_DESCRIPTOR_HANDLE srvHeapPtr = srvHeap.GetCPUDescriptorHandleForHeapStart();
|
|
gTextureSRV.InitializeAt(d3dDevice, texture, srvHeapPtr, &srvDesc);
|
|
srvHeapPtr.ptr += gD3D12Device.GetDescriptorHandleIncrementSize(XCEngine::RHI::DescriptorHeapType::CBV_SRV_UAV);
|
|
|
|
EndCommandList();
|
|
WaitForCompletionOfCommandList();
|
|
|
|
ShowWindow(hwnd, inShowCmd);
|
|
UpdateWindow(hwnd);
|
|
float color[] = { 0.5f,0.5f,0.5f,1.0f };
|
|
MSG msg;
|
|
DWORD last_time = timeGetTime();
|
|
DWORD appStartTime = last_time;
|
|
int frameCount = 0;
|
|
while (true) {
|
|
ZeroMemory(&msg, sizeof(MSG));
|
|
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
|
|
if (msg.message == WM_QUIT) {
|
|
break;
|
|
}
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
} else {
|
|
frameCount++;
|
|
WaitForCompletionOfCommandList();
|
|
DWORD current_time = timeGetTime();
|
|
DWORD frameTime = current_time - last_time;
|
|
DWORD timeSinceAppStartInMS = current_time - appStartTime;
|
|
last_time = current_time;
|
|
float frameTimeInSecond = float(frameTime) / 1000.0f;
|
|
float timeSinceAppStartInSecond = float(timeSinceAppStartInMS) / 1000.0f;
|
|
color[0] = timeSinceAppStartInSecond;
|
|
commandAllocator->Reset();
|
|
gCommandList.Reset(gCommandAllocator.GetCommandAllocator());
|
|
BeginRenderToSwapChain(gCommandList);
|
|
gCommandList.SetPipelineState(pso);
|
|
gCommandList.SetRootSignature(rootSignature);
|
|
gCommandList.SetDescriptorHeaps(_countof(descriptorHeaps), descriptorHeaps);
|
|
gCommandList.SetGraphicsRootConstantBufferView(0, gConstantBuffer.GetGPUVirtualAddress());
|
|
gCommandList.SetGraphicsRoot32BitConstants(1, 4, color, 0);
|
|
gCommandList.SetGraphicsRootDescriptorTable(2, srvHeap.GetGPUDescriptorHandleForHeapStart());
|
|
gCommandList.SetGraphicsRootShaderResourceView(3, gMaterialBuffer.GetGPUVirtualAddress());
|
|
gCommandList.SetPrimitiveTopology(XCEngine::RHI::PrimitiveTopology::TriangleList);
|
|
staticMeshComponent.Render(gCommandList);
|
|
|
|
// On screenshot frame, don't transition to PRESENT - keep RENDER_TARGET for screenshot
|
|
if (frameCount != 30) {
|
|
EndRenderToSwapChain(gCommandList);
|
|
}
|
|
EndCommandList();
|
|
|
|
// On screenshot frame, don't Present - we'll screenshot before next frame
|
|
if (frameCount != 30) {
|
|
gSwapChain.Present(0, 0);
|
|
}
|
|
|
|
// Screenshot after rendering is done
|
|
if (frameCount == 30) {
|
|
Log("[DEBUG] Saving screenshot at frame %d...\n", frameCount);
|
|
if (SaveScreenshot("screenshot.ppm", 1280, 720)) {
|
|
Log("[DEBUG] Screenshot saved to screenshot.ppm\n");
|
|
} else {
|
|
Log("[DEBUG] Failed to save screenshot!\n");
|
|
}
|
|
PostQuitMessage(0);
|
|
}
|
|
}
|
|
}
|
|
Logger::Get().Shutdown();
|
|
return 0;
|
|
}
|