Quellcode durchsuchen

feat: 添加 D3DBox 用于绘制立方体

nicetry12138 vor 1 Jahr
Ursprung
Commit
7572539509

+ 4 - 0
图形学/DirectX学习/README.md

@@ -667,6 +667,10 @@ HRESULT CreateFence(
 
 使用描述符来表示资源是因为 GPU 资源实际上都是一些普通的内存块,很多时候只希望将部分数据绑定到渲染流水线,那么从整个资源中将其选出?或者内存块如何使用?这些都是记录在描述符中的
 
+- 应用程序使用 `RTVHeap` 来存储 `RTV` 描述符
+- 每个 `RTV` 描述符都指向 `SwapChainBuffer` 中的一个缓冲区
+- `SwapChain` 管理这些缓冲区,并在适当的时候将它们呈现到屏幕上
+
 #### 资源转换
 
 为了实现常见的渲染效果,经常会通过 GPU 对某个资源 R 按顺序进行先写后读两种操作

+ 42 - 4
图形学/DirectX学习/src/D3DBox/D3DBox/D3DBox.cpp

@@ -4,10 +4,48 @@
 #include "D3DBox.h"
 #include "D3dApp.h"
 
+
+D3DBox::D3DBox(HINSTANCE hInstance) : D3DApp(hInstance)
+{
+}
+
+D3DBox::~D3DBox()
+{
+}
+
+bool D3DBox::Initialize()
+{
+	if (!D3DApp::Initialize())
+		return false;
+
+	return true;
+}
+
+void D3DBox::OnResize()
+{
+	D3DApp::OnResize();
+}
+
+void D3DBox::Update(const GameTimer& gt)
+{	
+	
+}
+
+void D3DBox::Draw(const GameTimer& gt)
+{
+
+}
+
+
 int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
-                     _In_opt_ HINSTANCE hPrevInstance,
-                     _In_ LPWSTR    lpCmdLine,
-                     _In_ int       nCmdShow)
+	_In_opt_ HINSTANCE hPrevInstance,
+	_In_ LPWSTR    lpCmdLine,
+	_In_ int       nCmdShow)
 {
-    return 0;
+	D3DBox app(hInstance);
+	if (!app.Initialize()) {
+		return 0;
+	}
+
+	return app.Run();
 }

+ 15 - 1
图形学/DirectX学习/src/D3DBox/D3DBox/D3DBox.h

@@ -1,3 +1,17 @@
 #pragma once
+#include "D3dApp.h"
 
-#include "resource.h"
+class D3DBox : public D3DApp
+{
+public:
+	D3DBox(HINSTANCE hInstance);
+	virtual ~D3DBox();
+
+	virtual bool Initialize() override;
+protected:
+
+private:
+	virtual void OnResize() override;
+	virtual void Update(const GameTimer& gt) override;
+	virtual void Draw(const GameTimer& gt) override;
+};

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

@@ -134,6 +134,7 @@
     <ClInclude Include="d3dx12.h" />
     <ClInclude Include="DDSTextureLoader.h" />
     <ClInclude Include="framework.h" />
+    <ClInclude Include="GameTimer.h" />
     <ClInclude Include="MathHelper.h" />
     <ClInclude Include="Resource.h" />
     <ClInclude Include="targetver.h" />
@@ -142,6 +143,7 @@
     <ClCompile Include="D3dApp.cpp" />
     <ClCompile Include="D3DBox.cpp" />
     <ClCompile Include="D3DUtil.cpp" />
+    <ClCompile Include="GameTimer.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="D3DBox.rc" />

+ 134 - 7
图形学/DirectX学习/src/D3DBox/D3DBox/D3dApp.cpp

@@ -67,6 +67,28 @@ void D3DApp::Set4XMassState(bool Value)
 
 int D3DApp::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) {
+				CalculateFrameState();
+				Update(mTimer);
+				Draw(mTimer);
+			}
+			else {
+				Sleep(100);
+			}
+		}
+	}
+
     return 0;
 }
 
@@ -117,6 +139,87 @@ void D3DApp::CreateRtvAndDsvDescriptorHeaps()
 
 void D3DApp::OnResize()
 {
+	// 检查资源是否初始化完毕
+	assert(md3dDevice);
+	assert(mSwapChain);
+	assert(mCommandAllocator);
+
+	// 清空命令队列
+	FlushCommandQueue();
+	mCommandList->Reset(mCommandAllocator.Get(), nullptr);
+
+	// 释放资源 重新创建
+	for (int i = 0; i < SwapChainBufferCount; ++i) {
+		mSwapChainBuffer[i].Reset();
+	}
+	mDepthStencilBuffer.Reset();
+	
+	// 重新设置交换链大小
+	mSwapChain->ResizeBuffers(SwapChainBufferCount, mClientWidth, mClientHeight, mBackBufferFormat, DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH);
+
+	// 创建 RTV
+	CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(mRtvHeap->GetCPUDescriptorHandleForHeapStart());
+	for (int i = 0; i < SwapChainBufferCount; ++i) {
+		mSwapChain->GetBuffer(i, IID_PPV_ARGS(&mSwapChainBuffer[i]));
+		md3dDevice->CreateRenderTargetView(mSwapChainBuffer[i].Get(), nullptr, rtvHandle);
+		rtvHandle.Offset(1, mRtvDescriptorSize);
+	}
+
+	// 创建 BSV
+	// 资源的维度、大小、格式等属性。在这里,它被用来定义深度模板缓冲区的特性,如宽度、高度、格式等
+	D3D12_RESOURCE_DESC depthStencilDesc;
+	depthStencilDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
+	depthStencilDesc.Width = mClientWidth;
+	depthStencilDesc.Height = mClientHeight;
+	depthStencilDesc.Alignment = 0;				// 指定资源对齐方式 一般设置为0 即默认对齐 一般是 64kb
+	depthStencilDesc.DepthOrArraySize = 1;		// 指定资源的深度(3D)或者数组大小(2D、1D),设置为1表示单层资源 不是资源数组
+	depthStencilDesc.MipLevels = 1;				// MIP 用于不同距离显示不同级别的细节,设置为1表示没有额外的 Mip 级别
+	depthStencilDesc.Format = DXGI_FORMAT_R24G8_TYPELESS;
+	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.0;
+	optClear.DepthStencil.Stencil = 0;
+
+	// 创建深度模板缓冲区,并将其初始化为常见状态,同时使用 optClear 作为优化的清除值
+	md3dDevice->CreateCommittedResource(&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), D3D12_HEAP_FLAG_NONE, &depthStencilDesc, D3D12_RESOURCE_STATE_COMMON, &optClear, IID_PPV_ARGS(mDepthStencilBuffer.GetAddressOf()));
+
+	// 如何从深度模板视图访问纹理的子资源。它定义了视图的格式和维度,以及如何解释深度模板数据
+	D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc;
+	dsvDesc.Flags = D3D12_DSV_FLAG_NONE;
+	dsvDesc.Format = mDepthStencilFormat;
+	dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
+	dsvDesc.Texture2D.MipSlice = 0;
+
+	// 创建一个深度模板视图,用于访问深度模板资源的数据
+	md3dDevice->CreateDepthStencilView(mDepthStencilBuffer.Get(), &dsvDesc, DepthStencilView());
+
+	// 更新资源状态为可写入
+	mCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(mDepthStencilBuffer.Get(), D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_DEPTH_WRITE));
+	
+	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 };
 }
 
 // 基本流程: 注册、创建、展示、更新
@@ -208,7 +311,7 @@ bool D3DApp::InitDirect3D()
 	CreateSwapChain();
 	CreateRtvAndDsvDescriptorHeaps();
 
-    return false;
+    return true;
 }
 
 void D3DApp::CreateCommandObjects()
@@ -230,23 +333,23 @@ void D3DApp::CreateSwapChain()
 	mSwapChain.Reset();
 
 	DXGI_SWAP_CHAIN_DESC sd;
-	sd.BufferCount = SwapChainBufferCount;
-	sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
 	sd.BufferDesc.Width = mClientWidth;
 	sd.BufferDesc.Height = mClientHeight;
-	sd.BufferDesc.RefreshRate.Denominator = 1;
 	sd.BufferDesc.RefreshRate.Numerator = 60;
-	sd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
-	sd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
+	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 = m4xMsaaState ? 4 : 1;
 	sd.SampleDesc.Quality = m4xMsaaState ? (m4xMsaaQuality - 1) : 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(md3dDevice.Get(), &sd, mSwapChain.GetAddressOf()));
+	ThrowIfFailed(mdxgiFactory->CreateSwapChain(mCommandQueue.Get(), &sd,mSwapChain.GetAddressOf()));
 }
 
 void D3DApp::FlushCommandQueue()
@@ -291,6 +394,30 @@ D3D12_CPU_DESCRIPTOR_HANDLE D3DApp::DepthStencilView() const
 
 void D3DApp::CalculateFrameState()
 {
+	static int frameCnt = 0;
+	static float timeElapsed = 0.0f;
+
+	frameCnt++;
+
+	// Compute averages over one second period.
+	if ((mTimer.TotalTime() - timeElapsed) >= 1.0f)
+	{
+		float fps = (float)frameCnt; // fps = frameCnt / 1
+		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());
+
+		// Reset for next average.
+		frameCnt = 0;
+		timeElapsed += 1.0f;
+	}
 }
 
 void D3DApp::LogAdapters()

+ 2 - 4
图形学/DirectX学习/src/D3DBox/D3DBox/D3dApp.h

@@ -6,15 +6,13 @@
 #endif
 
 #include "d3dUtil.h"
-//#include "GameTimer.h"
+#include "GameTimer.h"
 
 // Link necessary d3d12 libraries.
 #pragma comment(lib,"d3dcompiler.lib")
 #pragma comment(lib, "D3D12.lib")
 #pragma comment(lib, "dxgi.lib")
 
-class GameTimer;
-
 class D3DApp
 {
 protected:
@@ -82,7 +80,7 @@ protected:
 	bool		m4xMsaaState = false;	// 是否开启4x多重采样
 	UINT		m4xMsaaQuality = 0;		// 多重采样质量
 
-	//GameTimer	mTimer;					// 时间计时器
+	GameTimer	mTimer;					// 时间计时器
 
 	Microsoft::WRL::ComPtr<IDXGIFactory4> mdxgiFactory;	// DXGI 工厂
 	Microsoft::WRL::ComPtr<IDXGISwapChain> mSwapChain;	// 交换链

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

@@ -0,0 +1,127 @@
+//***************************************************************************************
+// GameTimer.cpp by Frank Luna (C) 2011 All Rights Reserved.
+//***************************************************************************************
+
+#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;
+}
+
+// Returns the total time elapsed since Reset() was called, NOT counting any
+// time when the clock is stopped.
+float GameTimer::TotalTime()const
+{
+	// If we are stopped, do not count the time that has passed since we stopped.
+	// Moreover, if we previously already had a pause, the distance 
+	// mStopTime - mBaseTime includes paused time, which we do not want to count.
+	// To correct this, we can subtract the paused time from mStopTime:  
+	//
+	//                     |<--paused time-->|
+	// ----*---------------*-----------------*------------*------------*------> time
+	//  mBaseTime       mStopTime        startTime     mStopTime    mCurrTime
+
+	if (mStopped)
+	{
+		return (float)(((mStopTime - mPausedTime) - mBaseTime) * mSecondsPerCount);
+	}
+
+	// The distance mCurrTime - mBaseTime includes paused time,
+	// which we do not want to count.  To correct this, we can subtract 
+	// the paused time from mCurrTime:  
+	//
+	//  (mCurrTime - mPausedTime) - mBaseTime 
+	//
+	//                     |<--paused time-->|
+	// ----*---------------*-----------------*------------*------> time
+	//  mBaseTime       mStopTime        startTime     mCurrTime
+
+	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);
+
+
+	// Accumulate the time elapsed between stop and start pairs.
+	//
+	//                     |<-------d------->|
+	// ----*---------------*-----------------*------------> time
+	//  mBaseTime       mStopTime        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;
+
+	// Time difference between this frame and the previous.
+	mDeltaTime = (mCurrTime - mPrevTime) * mSecondsPerCount;
+
+	// Prepare for next frame.
+	mPrevTime = mCurrTime;
+
+	// Force nonnegative.  The DXSDK's CDXUTTimer mentions that if the 
+	// processor goes into a power save mode or we get shuffled to another
+	// processor, then mDeltaTime can be negative.
+	if (mDeltaTime < 0.0)
+	{
+		mDeltaTime = 0.0;
+	}
+}
+

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

@@ -0,0 +1,34 @@
+//***************************************************************************************
+// GameTimer.h by Frank Luna (C) 2011 All Rights Reserved.
+//***************************************************************************************
+
+#ifndef GAMETIMER_H
+#define GAMETIMER_H
+
+class GameTimer
+{
+public:
+	GameTimer();
+
+	float TotalTime()const; // in seconds
+	float DeltaTime()const; // in seconds
+
+	void Reset(); // Call before message loop.
+	void Start(); // Call when unpaused.
+	void Stop();  // Call when paused.
+	void Tick();  // Call every frame.
+
+private:
+	double mSecondsPerCount;
+	double mDeltaTime;
+
+	__int64 mBaseTime;
+	__int64 mPausedTime;
+	__int64 mStopTime;
+	__int64 mPrevTime;
+	__int64 mCurrTime;
+
+	bool mStopped;
+};
+
+#endif // GAMETIMER_H