RHI、RDG相關記錄


在這里插入圖片描述

主要信息來自於 RDG101、UE Doc、一些博客 和 UE4.26的源碼。文章中的圖片主要來自於鏈接和源碼截圖。

https://epicgames.ent.box.com/s/ul1h44ozs0t2850ug0hrohlzm53kxwrz
https://docs.unrealengine.com/4.26/en-US/ProgrammingAndScripting/Rendering/Overview/
https://zhuanlan.zhihu.com/p/72086470
https://www.cnblogs.com/Saeru-Hikari/p/10898119.html
https://blog.csdn.net/leonwei/article/details/95527109
https://www.cnblogs.com/zhouxin/p/6418301.html
https://www.bilibili.com/read/cv5082163

1. RHI

RHI即Render Hardware Interface, 即渲染硬件接口, 是UE為實現跨平台而實現的一套API. 每個RHI接口都為OpenGL, Vulkan, DX11/12、Metal做了不同的實現. 在引擎初始化時使用的繪圖接口就已經確定, 引擎就可以確定RHI所使用接口的版本。

1.1 RHI實例

創建RHI實例的方法的聲明在DynamicRHI.h
在這里插入圖片描述
而這個方法的實現分散在對應平台的CPP文件中
在這里插入圖片描述
編譯時只保留目標平台的的源文件。

1.2 主要功能

1.2.1 渲染特性查詢

對應在RHI.hRHI.cpp中,包括當前顯卡硬件的代號以及驅動版本,深度測試時獲取深度, 體紋理, 硬件合並渲染, 對MSAA的支持, 對各種RenderTarget格式的支持程度, 乃至於光柵化等。

比如查詢是否支持細分的方法:

// helper to check that the shader platform supports tessellation.
RHI_API bool RHISupportsTessellation(const FStaticShaderPlatform Platform);
...
RHI_API bool RHISupportsTessellation(const FStaticShaderPlatform Platform)
{
	if (IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM5))
	{
		return (Platform == SP_PCD3D_SM5) || (Platform == SP_XBOXONE_D3D12) || (Platform == SP_METAL_SM5) || (IsVulkanSM5Platform(Platform));
	}
	return false;
}

在使用Vulkan時,其依據就來自於創建物理設備之后,邏輯設備創建之前,Vulkan對Feature的一次查詢

void FVulkanDevice::InitGPU(int32 DeviceIndex)
{
	LLM_SCOPE_VULKAN(ELLMTagVulkan::VulkanMisc);

	// Query features
	VulkanRHI::vkGetPhysicalDeviceFeatures(Gpu, &PhysicalFeatures);
	
	...
	CreateDevice();
	...
}

實際上,RHI模塊大多數實現都在對應的 XXXRHI模塊中。

1.2.2 API基礎功能

對應在DynamicRHI中,基本上用API寫渲染器時所用到的功能都有,創建 Shader、Buffer、SamplerState,Mips生成等等。
在這里插入圖片描述

1.2.3 渲染資源封裝

對應在RHIResources中。
基類FRHIResource封裝了一些基礎方法,主要有釋放、延時釋放、提交狀態、引用計數、可用檢測等等。

class RHI_API FRHIResource
{
public:
	...
	FORCEINLINE_DEBUGGABLE uint32 AddRef() const;
	FORCEINLINE_DEBUGGABLE uint32 Release() const;
	FORCEINLINE_DEBUGGABLE uint32 GetRefCount() const;
	void DoNoDeferDelete();
	static void FlushPendingDeletes(bool bFlushDeferredDeletes = false);
	bool IsValid() const;
	void SetCommitted(bool bInCommitted);
	bool IsCommitted() const;
	...
private:
	mutable FThreadSafeCounter NumRefs;
	mutable int32 MarkedForDelete;
	bool bDoNotDeferDelete;
	bool bCommitted;
	static uint32 CurrentFrame;
};

所有DynamicRHI.h中能創建的資源都有一個對應的FRHIResource子類:

typedef TRefCountPtr<frhisamplerstate> FSamplerStateRHIRef;
typedef TRefCountPtr<frhirasterizerstate> FRasterizerStateRHIRef;
typedef TRefCountPtr<frhidepthstencilstate> FDepthStencilStateRHIRef;
typedef TRefCountPtr<frhiblendstate> FBlendStateRHIRef;
typedef TRefCountPtr<frhivertexdeclaration> FVertexDeclarationRHIRef;
typedef TRefCountPtr<frhivertexshader> FVertexShaderRHIRef;
typedef TRefCountPtr<frhihullshader> FHullShaderRHIRef;
typedef TRefCountPtr<frhidomainshader> FDomainShaderRHIRef;
typedef TRefCountPtr<frhipixelshader> FPixelShaderRHIRef;
typedef TRefCountPtr<frhigeometryshader> FGeometryShaderRHIRef;
typedef TRefCountPtr<frhicomputeshader> FComputeShaderRHIRef;
typedef TRefCountPtr<frhiraytracingshader>          FRayTracingShaderRHIRef;
typedef TRefCountPtr<frhicomputefence>	FComputeFenceRHIRef;
typedef TRefCountPtr<frhiboundshaderstate> FBoundShaderStateRHIRef;
typedef TRefCountPtr<frhiuniformbuffer> FUniformBufferRHIRef;
typedef TRefCountPtr<frhiindexbuffer> FIndexBufferRHIRef;
typedef TRefCountPtr<frhivertexbuffer> FVertexBufferRHIRef;
typedef TRefCountPtr<frhistructuredbuffer> FStructuredBufferRHIRef;
typedef TRefCountPtr<frhitexture> FTextureRHIRef;
typedef TRefCountPtr<frhitexture2d> FTexture2DRHIRef;
typedef TRefCountPtr<frhitexture2darray> FTexture2DArrayRHIRef;
typedef TRefCountPtr<frhitexture3d> FTexture3DRHIRef;
typedef TRefCountPtr<frhitexturecube> FTextureCubeRHIRef;
typedef TRefCountPtr<frhitexturereference> FTextureReferenceRHIRef;
typedef TRefCountPtr<frhirenderquery> FRenderQueryRHIRef;
typedef TRefCountPtr<frhirenderquerypool> FRenderQueryPoolRHIRef;
typedef TRefCountPtr<frhitimestampcalibrationquery> FTimestampCalibrationQueryRHIRef;
typedef TRefCountPtr<frhigpufence>	FGPUFenceRHIRef;
typedef TRefCountPtr<frhiviewport> FViewportRHIRef;
typedef TRefCountPtr<frhiunorderedaccessview> FUnorderedAccessViewRHIRef;
typedef TRefCountPtr<frhishaderresourceview> FShaderResourceViewRHIRef;
typedef TRefCountPtr<frhigraphicspipelinestate> FGraphicsPipelineStateRHIRef;
typedef TRefCountPtr<frhiraytracingpipelinestate> FRayTracingPipelineStateRHIRef;

每個都根據自身需要額外添加了一些方法和成員變量,比如有關Layout,Usage,SizeXY的等等。

1.2.4 渲染指令

1.2.4.1 FRHICommandBase 與 FRHICommand

FRHICommandBase CommanList鏈表的一個元素,ExecuteAndDestruct用於執行當前指令,而Next 指向了下一條指令。

struct FRHICommandBase
{
	FRHICommandBase* Next = nullptr;
	virtual void ExecuteAndDestruct(FRHICommandListBase& CmdList, FRHICommandListDebugContext& DebugContext) = 0;
};

FRHICommand是一個模板類,需要模板TCmd實現方法Execute. 而此子類在執行ExecuteAndDestruct時會退化到參數類型TCmd, 執行TCmd下的Execute方法后自動析構, 這個過程也就實現了所謂"ExecuteAndDestruct"

template<typename tcmd,="" typename="" nametype="FUnnamedRhiCommand">
struct FRHICommand : public FRHICommandBase
{
#if RHICOMMAND_CALLSTACK
	uint64 StackFrames[16];

	FRHICommand()
	{
		FPlatformStackWalk::CaptureStackBackTrace(StackFrames, 16);
	}
#endif

	void ExecuteAndDestruct(FRHICommandListBase& CmdList, FRHICommandListDebugContext& Context) override final
	{
		TRACE_CPUPROFILER_EVENT_SCOPE_ON_CHANNEL_STR(NameType::TStr(), RHICommandsChannel);
		
		TCmd *ThisCmd = static_cast<tcmd*>(this);
#if RHI_COMMAND_LIST_DEBUG_TRACES
		ThisCmd->StoreDebugInfo(Context);
#endif
		ThisCmd->Execute(CmdList);
		ThisCmd->~TCmd();
	}

	virtual void StoreDebugInfo(FRHICommandListDebugContext& Context) {};
};

1.2.4.2 FRHICommandListBase與 FRHICommandList

FRHICommandListBase中定義了相應FRHICommandBase的鏈表實現,以及定義一些上下文如IRHICommandContext , IRHIComputeContext
還定義了渲染線程和RHI線程交互的API:

...
void QueueAsyncCommandListSubmit(FGraphEventRef& AnyThreadCompletionEvent, class FRHICommandList* CmdList);
void QueueParallelAsyncCommandListSubmit(FGraphEventRef* AnyThreadCompletionEvents, bool bIsPrepass, class FRHICommandList** CmdLists, int32* NumDrawsIfKnown, int32 Num, int32 MinDrawsPerTranslate, bool bSpewMerge);
void QueueRenderThreadCommandListSubmit(FGraphEventRef& RenderThreadCompletionEvent, class FRHICommandList* CmdList);
void QueueCommandListSubmit(class FRHICommandList* CmdList);
void AddDispatchPrerequisite(const FGraphEventRef& Prereq);
void WaitForTasks(bool bKnownToBeComplete = false);
void WaitForDispatch();
void WaitForRHIThreadTasks();
void HandleRTThreadTaskCompletion(const FGraphEventRef& MyCompletionGraphEvent);
...

RHI本身相應的FRHICommandBaseFRHICommandBaseList都是存放在渲染線程中,RHI線程可以用於在渲染線程中同步執行異步的復雜操作。比如:壓入很多FRHICommandBase到渲染線程中執行;有些操作可以放入RHI線程中與渲染線程一起執行;在某段FRHICommandBase前,調用WaitForTasks等同步渲染線程與RHI線程 等等操作。

FRHICommandList中有所有用於渲染的API指令封裝,一般有二種方法,一種是插入FRHICommandListBase鏈表,一種是直接調用相應渲染平台對應FDynamicRHI中的實現。
比如:

...
FORCEINLINE_DEBUGGABLE void SetShaderTexture(FRHIComputeShader* Shader, uint32 TextureIndex, FRHITexture* Texture)
{
	ValidateBoundShader(Shader);
	if (Bypass())
	{
		GetComputeContext().RHISetShaderTexture(Shader, TextureIndex, Texture);
		return;
	}
	ALLOC_COMMAND(FRHICommandSetShaderTexture<frhicomputeshader>)(Shader, TextureIndex, Texture);
}
...

2. RDG

PPT上寫的挺詳細,這里主要總結一下。

2.1 Shader Parameters

Shader參數設置方法,大體上分兩種,第一種是只有某個shader能訪問的局部shader parmeters,另一種是能夠全局訪問的uniformbuffer,這兩種是使用不同的宏來進行實現的。

2.1.1 如何用宏設置Shader Parameter:

在這里插入圖片描述

2.1.2 自動對齊規則

每個成員都是按照其大小的下一個冪進行對齊的,但前提是大於4個字節:
指針是8字節對齊的(即使在32位平台上也是如此);
浮點、uint32、int32是四字節對齊的;
FVector2D,FIntPoint是8字節對齊的;
FVector和FVector 4是16字節對齊的。
每個成員的自動對齊將不可避免地創建填充,如下面的注釋所示:
在這里插入圖片描述有自動合並對齊功能,但數組沒有
在這里插入圖片描述
可以手動通過調整順序來減少自動填充的空間浪費
在這里插入圖片描述

2.1.3 Parameter與Shader綁定

SHADER_USE_PARAMETER_STRUCT
在這里插入圖片描述
直接把宏放在類里,就不用碼using那行了

2.1.4 如何設置參數

在這里插入圖片描述

2.1.5 Uniform Buffer

在這里插入圖片描述
UE會自動生成.ush文件,上圖中IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT的第二個參數,是UBO在Shader里的名字。
然后在上面的Shader Parameters里引用UBO:
在這里插入圖片描述

2.2 Render Graph Basics

2.2.1 創建和設置RenderGraph

下圖中是創建FRDGBuilder與執行的代碼。中間需要填充的便是具體執行內容。
在這里插入圖片描述

2.2.2 創建和設置Texture

2.2.2.1 創建Texture

在這里插入圖片描述
注意這里是渲染線程,Texture實際上是異步在RHI線程創建的,所以現在拿到的是個句柄。

2.2.2.2 創建SRV,UAV

在這里插入圖片描述在這里插入圖片描述
着色器資源視圖 (SRV) 和無序的訪問視圖 (UAV)是DX中的概念,DX沒有碰過不是很了解,不過看概念映射到Vulkan應該是對應的Image View、Usage標志。

2.2.2.3 將Texture綁Shader上

在這里插入圖片描述

2.2.3 在RDG上添加Pass

在這里插入圖片描述分配參數→設置參數→管理參數到Pass

2.2.4 建立和綁定 Color、DepthStencil Target

首先要做的是將RENDER_TARGET_BINDING_SLOTS()添加到Shader Parameters里
在這里插入圖片描述
然后設置參數時設置到RenderTargets數組里
在這里插入圖片描述
DepthStencil是單獨的
在這里插入圖片描述

除了上面這種方法之外,還可以用之前UAV的方式:
在這里插入圖片描述

2.2.5 IPooledRenderTarget的使用

控制紋理的分配的接口,有時需要將現有資源導入到RDG中(特別是在RDG轉換過程中)。Builder公開RealStices外部紋理,它返回由現有渲染目標支持的RDG紋理實例:
在這里插入圖片描述
還可以從FRDGTexture中提取rt指針。這允許您跨RDG調用保留資源的內容。但是,提取會延遲到圖形執行完畢;這是因為在執行期間可能會根據圖形中資源的生存期來分配資源。因此,API公開了QueueTextureExtraction,它允許您提供一個指針,該指針將在graph執行時填充:
在這里插入圖片描述

2.2.6 創建和設置Buffer

2.2.6.1 普通Buffer

在這里插入圖片描述
如果一個buffer使用了SHADER_PARAMETER_RDG_BUFFER_SRV().着色器只能通過SRV讀取:
在這里插入圖片描述

2.2.6.1 Indirect Draw/Dispatch Buffer

間接的draw / dispatch buffer比較特殊獨特,因為它們不是由着色器直接使用的。相反,將它們聲明為pass參數上的RDG緩沖區,然后直接在pass中使用RHI間接繪制緩沖區:
在這里插入圖片描述

2.3 Pass Debugging and Methodology

未完待續

</tcmd*>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM