فهرست منبع

feat: 添加 D3DBox 的渲染流水线初始化

nicetry12138 1 سال پیش
والد
کامیت
aed6ea98b4

+ 157 - 0
图形学/DirectX学习/src/D3DBox/D3DBox/D3DBox.cpp

@@ -18,6 +18,13 @@ bool D3DBox::Initialize()
 	if (!D3DApp::Initialize())
 		return false;
 
+	BuildDescriptorHeaps();
+	BuildConstantBuffers();
+	BuildRootSignature();
+	BuildShaderAndInputLayout();
+	BuildBoxGeometry();
+	BuildPSO();
+
 	return true;
 }
 
@@ -35,6 +42,156 @@ void D3DBox::Draw(const GameTimer& gt)
 {
 }
 
+void D3DBox::OnMouseDown(WPARAM btnState, int x, int y)
+{
+}
+
+void D3DBox::OnMouseUp(WPARAM btnState, int x, int y)
+{
+}
+
+void D3DBox::OnMouseMove(WPARAM btnStae, int x, int y)
+{
+}
+
+void D3DBox::BuildDescriptorHeaps()
+{
+	D3D12_DESCRIPTOR_HEAP_DESC cbvHeapDesc;
+	cbvHeapDesc.NodeMask = 0;
+	cbvHeapDesc.NumDescriptors = 1;
+	cbvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
+	cbvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
+	md3dDevice->CreateDescriptorHeap(&cbvHeapDesc, IID_PPV_ARGS(&mCbvHeap));
+}
+
+void D3DBox::BuildConstantBuffers()
+{
+	mObjectCB = std::make_unique<UploadBuffer<ObjectConstants>>(md3dDevice.Get(), 1, true);
+	
+	
+	UINT objCBByteSize = d3dUtil::CalcConstantBufferByteSize(sizeof(ObjectConstants));	// 要求大小是硬件最低配置 256b 的整数倍
+	
+	D3D12_GPU_VIRTUAL_ADDRESS cbAddress = mObjectCB->Resource()->GetGPUVirtualAddress();
+	
+	int boxCBBufferIndex = 0;
+	cbAddress += boxCBBufferIndex * objCBByteSize;		// 计算地址偏移, 如果缓冲区中存储多个 ObjectConstants 将指定数据创建描述符
+
+	D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc;
+	cbvDesc.BufferLocation = cbAddress;
+	cbvDesc.SizeInBytes = objCBByteSize;
+
+	md3dDevice->CreateConstantBufferView(&cbvDesc, mCbvHeap->GetCPUDescriptorHandleForHeapStart());	// 创建CBV描述符
+	// 如果 cbvheap 绑定过其他 cbv 可能需要通过 offset 计算偏移
+}
+
+void D3DBox::BuildRootSignature()
+{
+	CD3DX12_ROOT_PARAMETER slotRootParameter[1];
+
+	CD3DX12_DESCRIPTOR_RANGE cbvTable;
+	cbvTable.Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 0);		// 绑定一个 CBV 到第 0 个寄存器中
+	slotRootParameter[0].InitAsDescriptorTable(1, &cbvTable);
+
+	// 创建一个带有 一个 常量表 的根标签
+	CD3DX12_ROOT_SIGNATURE_DESC rootSigDesc(1, slotRootParameter, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
+
+	ComPtr<ID3DBlob> serializedRootSig = nullptr;
+	ComPtr<ID3DBlob> errorBlob = nullptr;
+
+	HRESULT hr = D3D12SerializeRootSignature(&rootSigDesc, D3D_ROOT_SIGNATURE_VERSION_1, serializedRootSig.GetAddressOf(), errorBlob.GetAddressOf());
+
+	if (errorBlob != nullptr)
+	{
+		::OutputDebugStringA((char*)errorBlob->GetBufferPointer());
+	}
+	ThrowIfFailed(hr);
+
+	md3dDevice->CreateRootSignature(0, serializedRootSig->GetBufferPointer(), serializedRootSig->GetBufferSize(), IID_PPV_ARGS(&mRootSignature));
+}
+
+void D3DBox::BuildShaderAndInputLayout()
+{
+	mVSByteCode = d3dUtil::CompileShader(L"res/color.hlsl", nullptr, "VS", "vs_5_0");
+	mPSByteCode = d3dUtil::CompileShader(L"res/color.hlsl", nullptr, "PS", "ps_5_0");
+
+	mInputLayout =
+	{
+		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
+		{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }
+	};
+}
+
+void D3DBox::BuildPSO()
+{
+	D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc;
+
+	ZeroMemory(&psoDesc, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC));
+	psoDesc.InputLayout = { mInputLayout.data(), (UINT)mInputLayout.size() };
+	psoDesc.pRootSignature = mRootSignature.Get();
+	psoDesc.VS = { mVSByteCode->GetBufferPointer(), mVSByteCode->GetBufferSize() };
+	psoDesc.PS = { mPSByteCode->GetBufferPointer(), mPSByteCode->GetBufferSize() };
+	psoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
+	psoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
+	psoDesc.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);
+	psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
+	psoDesc.SampleMask = UINT_MAX;
+	psoDesc.NumRenderTargets = 1;
+	psoDesc.RTVFormats[0] = mBackBufferFormat;
+	psoDesc.SampleDesc.Count = m4xMsaaState ? 4 : 1;
+	psoDesc.SampleDesc.Quality = m4xMsaaState ? (m4xMsaaQuality - 1) : 0;
+	psoDesc.DSVFormat = mDepthStencilFormat;
+
+	md3dDevice->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&mPSO));
+}
+
+void D3DBox::BuildBoxGeometry()
+{
+	std::array<Vertex, 8> vertices =
+	{
+		Vertex({ XMFLOAT3(-1.0f, -1.0f, -1.0f), XMFLOAT4(Colors::White) }),
+		Vertex({ XMFLOAT3(-1.0f, +1.0f, -1.0f), XMFLOAT4(Colors::Black) }),
+		Vertex({ XMFLOAT3(+1.0f, +1.0f, -1.0f), XMFLOAT4(Colors::Red) }),
+		Vertex({ XMFLOAT3(+1.0f, -1.0f, -1.0f), XMFLOAT4(Colors::Green) }),
+		Vertex({ XMFLOAT3(-1.0f, -1.0f, +1.0f), XMFLOAT4(Colors::Blue) }),
+		Vertex({ XMFLOAT3(-1.0f, +1.0f, +1.0f), XMFLOAT4(Colors::Yellow) }),
+		Vertex({ XMFLOAT3(+1.0f, +1.0f, +1.0f), XMFLOAT4(Colors::Cyan) }),
+		Vertex({ XMFLOAT3(+1.0f, -1.0f, +1.0f), XMFLOAT4(Colors::Magenta) })
+	};
+
+	std::array<std::uint16_t, 36> indices =
+	{
+		// front face
+		0, 1, 2,
+		0, 2, 3,
+
+		// back face
+		4, 6, 5,
+		4, 7, 6,
+
+		// left face
+		4, 5, 1,
+		4, 1, 0,
+
+		// right face
+		3, 2, 6,
+		3, 6, 7,
+
+		// top face
+		1, 5, 6,
+		1, 6, 2,
+
+		// bottom face
+		4, 0, 3,
+		4, 3, 7
+	};
+
+	const UINT vbByteSize = (UINT)vertices.size() * sizeof(Vertex);
+	const UINT ibByteSize = (UINT)indices.size() * sizeof(std::uint16_t);
+
+	mBoxGeo = std::make_unique<MeshGeometry>();
+	mBoxGeo->Name = "boxGeo";
+}
+
 
 int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
 	_In_opt_ HINSTANCE hPrevInstance,

+ 56 - 0
图形学/DirectX学习/src/D3DBox/D3DBox/D3DBox.h

@@ -1,5 +1,22 @@
 #pragma once
 #include "D3dApp.h"
+#include "MathHelper.h"
+#include "UploadBuffer.h"
+
+using Microsoft::WRL::ComPtr;
+using namespace DirectX;
+using namespace DirectX::PackedVector;
+
+struct Vertex
+{
+	XMFLOAT3 Pos;
+	XMFLOAT4 Color;
+};
+
+struct ObjectConstants
+{
+	XMFLOAT4X4 WorldViewProj = MathHelper::Identity4x4();
+};
 
 class D3DBox : public D3DApp
 {
@@ -7,6 +24,9 @@ public:
 	D3DBox(HINSTANCE hInstance);
 	virtual ~D3DBox();
 
+	D3DBox& operator =(const D3DBox& rhs) = delete;
+	D3DBox(const D3DBox& rhs) = delete;
+
 	virtual bool Initialize() override;
 protected:
 
@@ -14,4 +34,40 @@ private:
 	virtual void OnResize() override;
 	virtual void Update(const GameTimer& gt) override;
 	virtual void Draw(const GameTimer& gt) override;
+
+	virtual void OnMouseDown(WPARAM btnState, int x, int y);
+	virtual void OnMouseUp(WPARAM btnState, int x, int y);
+	virtual void OnMouseMove(WPARAM btnStae, int x, int y);
+
+	void BuildDescriptorHeaps();	// 创建 CSV SRV UAV 堆描述符
+	void BuildConstantBuffers();	// 创建 CBV 常量缓冲区视图
+	void BuildRootSignature();		// 创建 根标签 用于绑定常量缓冲区
+	void BuildShaderAndInputLayout();	// 创建 Shader 和输入布局
+	void BuildPSO();				// 创建 Pipeline State Object 流水线状态对象,绑定流水线资源
+	void BuildBoxGeometry();		// 创建盒子的顶点缓冲和索引缓冲
+
+private:
+	ComPtr<ID3D12RootSignature> mRootSignature = nullptr;
+	ComPtr<ID3D12DescriptorHeap> mCbvHeap = nullptr;
+
+	std::unique_ptr<UploadBuffer<ObjectConstants>> mObjectCB = nullptr;
+	
+	std::unique_ptr<MeshGeometry> mBoxGeo = nullptr;	// Box 的几何坐标
+
+	ComPtr<ID3DBlob> mVSByteCode = nullptr;				// 顶点着色器编译后的字节码
+	ComPtr<ID3DBlob> mPSByteCode = nullptr;				// 像素着色器编译后的字节码
+
+	std::vector<D3D12_INPUT_ELEMENT_DESC> mInputLayout;	// 输入布局
+
+	ComPtr<ID3D12PipelineState> mPSO = nullptr;			// 流水线状态对象
+
+	XMFLOAT4X4 mWorld = MathHelper::Identity4x4();
+	XMFLOAT4X4 mView = MathHelper::Identity4x4();
+	XMFLOAT4X4 mProj = MathHelper::Identity4x4();
+
+	float mTheta = 1.5f * XM_PI;
+	float mPhi = XM_PIDIV4;
+	float mRadius = 5.0f;
+
+	POINT mLastMousePos;
 };

+ 11 - 0
图形学/DirectX学习/src/D3DBox/D3DBox/D3DBox.vcxproj

@@ -110,6 +110,10 @@
       <SubSystem>Windows</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
     </Link>
+    <FxCompile>
+      <ShaderType>
+      </ShaderType>
+    </FxCompile>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
     <ClCompile>
@@ -137,7 +141,9 @@
     <ClInclude Include="GameTimer.h" />
     <ClInclude Include="MathHelper.h" />
     <ClInclude Include="Resource.h" />
+    <ClInclude Include="res\UploadBuffer.h" />
     <ClInclude Include="targetver.h" />
+    <ClInclude Include="UploadBuffer.h" />
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="D3dApp.cpp" />
@@ -152,6 +158,11 @@
     <Image Include="D3DBox.ico" />
     <Image Include="small.ico" />
   </ItemGroup>
+  <ItemGroup>
+    <None Include="res\color.hlsl">
+      <FileType>Document</FileType>
+    </None>
+  </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>

+ 20 - 0
图形学/DirectX学习/src/D3DBox/D3DBox/D3DBox.vcxproj.filters

@@ -13,6 +13,9 @@
       <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
       <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
     </Filter>
+    <Filter Include="res">
+      <UniqueIdentifier>{a93ece64-06dd-42bb-b4e5-f746fe4fa8bc}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="framework.h">
@@ -42,6 +45,15 @@
     <ClInclude Include="D3DUtil.h">
       <Filter>头文件</Filter>
     </ClInclude>
+    <ClInclude Include="GameTimer.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="res\UploadBuffer.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="UploadBuffer.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="D3DBox.cpp">
@@ -53,6 +65,9 @@
     <ClCompile Include="D3DUtil.cpp">
       <Filter>源文件</Filter>
     </ClCompile>
+    <ClCompile Include="GameTimer.cpp">
+      <Filter>源文件</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="D3DBox.rc">
@@ -67,4 +82,9 @@
       <Filter>资源文件</Filter>
     </Image>
   </ItemGroup>
+  <ItemGroup>
+    <None Include="res\color.hlsl">
+      <Filter>res</Filter>
+    </None>
+  </ItemGroup>
 </Project>

+ 132 - 3
图形学/DirectX学习/src/D3DBox/D3DBox/D3dApp.cpp

@@ -1,5 +1,6 @@
 #include "D3dApp.h"
 #include <Windows.h>
+#include <windowsx.h>
 
 using Microsoft::WRL::ComPtr;
 using namespace std;
@@ -111,10 +112,138 @@ LRESULT D3DApp::MsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
 {
 	switch (msg)
 	{
-	default:
-		break;
+		// WM_ACTIVATE is sent when the window is activated or deactivated.  
+		// We pause the game when the window is deactivated and unpause it 
+		// when it becomes active.  
+	case WM_ACTIVATE:
+		if (LOWORD(wParam) == WA_INACTIVE)
+		{
+			mAppPaused = true;
+			mTimer.Stop();
+		}
+		else
+		{
+			mAppPaused = false;
+			mTimer.Start();
+		}
+		return 0;
+
+		// WM_SIZE is sent when the user resizes the window.  
+	case WM_SIZE:
+		// Save the new client area dimensions.
+		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)
+			{
+
+				// Restoring from minimized state?
+				if (mMinimized)
+				{
+					mAppPaused = false;
+					mMinimized = false;
+					OnResize();
+				}
+
+				// Restoring from maximized state?
+				else if (mMaximized)
+				{
+					mAppPaused = false;
+					mMaximized = false;
+					OnResize();
+				}
+				else if (mResizing)
+				{
+					// If user is dragging the resize bars, we do not resize 
+					// the buffers here because as the user continuously 
+					// drags the resize bars, a stream of WM_SIZE messages are
+					// sent to the window, and it would be pointless (and slow)
+					// to resize for each WM_SIZE message received from dragging
+					// the resize bars.  So instead, we reset after the user is 
+					// done resizing the window and releases the resize bars, which 
+					// sends a WM_EXITSIZEMOVE message.
+				}
+				else // API call such as SetWindowPos or mSwapChain->SetFullscreenState.
+				{
+					OnResize();
+				}
+			}
+		}
+		return 0;
+
+		// WM_EXITSIZEMOVE is sent when the user grabs the resize bars.
+	case WM_ENTERSIZEMOVE:
+		mAppPaused = true;
+		mResizing = true;
+		mTimer.Stop();
+		return 0;
+
+		// WM_EXITSIZEMOVE is sent when the user releases the resize bars.
+		// Here we reset everything based on the new window dimensions.
+	case WM_EXITSIZEMOVE:
+		mAppPaused = false;
+		mResizing = false;
+		mTimer.Start();
+		OnResize();
+		return 0;
+
+		// WM_DESTROY is sent when the window is being destroyed.
+	case WM_DESTROY:
+		PostQuitMessage(0);
+		return 0;
+
+		// The WM_MENUCHAR message is sent when a menu is active and the user presses 
+		// a key that does not correspond to any mnemonic or accelerator key. 
+	case WM_MENUCHAR:
+		// Don't beep when we alt-enter.
+		return MAKELRESULT(0, MNC_CLOSE);
+
+		// Catch this message so to prevent the window from becoming too small.
+	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) {
+			Set4XMassState(!m4xMsaaState);
+		}
+
+		return 0;
 	}
-    return DefWindowProc(hWnd, msg, wParam, lParam);
+
+	return DefWindowProc(hWnd, msg, wParam, lParam);
 }
 
 void D3DApp::CreateRtvAndDsvDescriptorHeaps()

+ 64 - 0
图形学/DirectX学习/src/D3DBox/D3DBox/UploadBuffer.h

@@ -0,0 +1,64 @@
+#pragma once
+
+#include "D3DUtil.h"
+
+template<typename T>
+class UploadBuffer
+{
+public:
+	UploadBuffer(ID3D12Device* device, UINT elementCount, bool isConstantBuffer) :
+		mIsConstantBuffer(isConstantBuffer)
+	{
+		mElementByteSize = sizeof(T);
+
+		// Constant buffer elements need to be multiples of 256 bytes.
+		// This is because the hardware can only view constant data 
+		// at m*256 byte offsets and of n*256 byte lengths. 
+		// typedef struct D3D12_CONSTANT_BUFFER_VIEW_DESC {
+		// UINT64 OffsetInBytes; // multiple of 256
+		// UINT   SizeInBytes;   // multiple of 256
+		// } D3D12_CONSTANT_BUFFER_VIEW_DESC;
+		if (isConstantBuffer)
+			mElementByteSize = d3dUtil::CalcConstantBufferByteSize(sizeof(T));
+
+		ThrowIfFailed(device->CreateCommittedResource(
+			&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
+			D3D12_HEAP_FLAG_NONE,
+			&CD3DX12_RESOURCE_DESC::Buffer(mElementByteSize * elementCount),
+			D3D12_RESOURCE_STATE_GENERIC_READ,
+			nullptr,
+			IID_PPV_ARGS(&mUploadBuffer)));
+
+		ThrowIfFailed(mUploadBuffer->Map(0, nullptr, reinterpret_cast<void**>(&mMappedData)));
+
+		// We do not need to unmap until we are done with the resource.  However, we must not write to
+		// the resource while it is in use by the GPU (so we must use synchronization techniques).
+	}
+
+	UploadBuffer(const UploadBuffer& rhs) = delete;
+	UploadBuffer& operator=(const UploadBuffer& rhs) = delete;
+	~UploadBuffer()
+	{
+		if (mUploadBuffer != nullptr)
+			mUploadBuffer->Unmap(0, nullptr);
+
+		mMappedData = nullptr;
+	}
+
+	ID3D12Resource* Resource()const
+	{
+		return mUploadBuffer.Get();
+	}
+
+	void CopyData(int elementIndex, const T& data)
+	{
+		memcpy(&mMappedData[elementIndex * mElementByteSize], &data, sizeof(T));
+	}
+
+private:
+	Microsoft::WRL::ComPtr<ID3D12Resource> mUploadBuffer;
+	BYTE* mMappedData = nullptr;
+
+	UINT mElementByteSize = 0;
+	bool mIsConstantBuffer = false;
+};

+ 1 - 11
图形学/DirectX学习/src/D3DBox/D3DBox/res/color.hlsl

@@ -1,11 +1,3 @@
-struct ObjectConstants
-{
-    float4x4 gWorldViewProj;
-    uint matIndex;
-};
-
-ConstantBuffer<ObjectConstants> gObjectConstants : register(b0);
-
 cbuffer cbPerObject : register(b0)
 {
     float4x4 gWorldViewProj;
@@ -39,6 +31,4 @@ VertexOut VS(VertexIn vin)
 float4 PS(VertexOut pin) : SV_Target
 {
     return pin.Color;
-}
-
-
+}