RHI全稱是Render Hardware Interface(渲染硬件接口),是UE渲染體系中非常基礎且重要的模塊,封裝了眾多圖形API(DirectX、OpenGL、Vulkan、Metal)之間的差異。
基於D3D11 API設計而成,包含了資源管理(Shader、Texture、VertexBuffer等)和圖形API封裝(DrawIndexedPrimitive、Clear、SetTexture等)。
對Game和Renderer模塊提供了簡便且一致的概念、數據、資源和接口,實現一份渲染代碼跑在多個平台的目標。
RHI相關的測試代碼:UnrealEngine\Engine\Plugins\Tests\RHITests\Source\RHITests
FRHIResource
實現RHI資源的平台無關性
基類FRHIResource提供了幾種功能:引用計數、延遲刪除及追蹤、運行時標記。
// Engine\Source\Runtime\RHI\Public\RHIResources.h class RHI_API FRHIResource { public: FRHIResource(bool InbDoNotDeferDelete = false); virtual ~FRHIResource() { check(PlatformNeedsExtraDeletionLatency() || (NumRefs.GetValue() == 0 && (CurrentlyDeleting == this || bDoNotDeferDelete || Bypass()))); // this should not have any outstanding refs } // 資源的引用計數. uint32 AddRef() const; /* 增加引用計數 */ uint32 Release() const /* 減少引用計數 */ { int32 NewValue = NumRefs.Decrement(); // 計數-1 if (NewValue == 0) // 如果計數為0, 處理資源刪除 { if (!DeferDelete()) // 非延遲刪除, 直接delete { delete this; } else // 延遲刪除模式 { // 加入待刪除列表. if (FPlatformAtomics::InterlockedCompareExchange(&MarkedForDelete, 1, 0) == 0) { PendingDeletes.Push(const_cast<FRHIResource*>(this)); } } } return uint32(NewValue); // 返回新的值 } uint32 GetRefCount() const; /* 獲取引用計數 */ // 靜態接口. static void FlushPendingDeletes(bool bFlushDeferredDeletes = false) { FRHICommandListImmediate& RHICmdList = FRHICommandListExecutor::GetImmediateCommandList(); // 在刪除RHI資源之前, 先確保命令列表已被刷新到GPU. RHICmdList.ImmediateFlush(EImmediateFlushType::FlushRHIThread); // 確保沒有等待的任務. FRHICommandListExecutor::CheckNoOutstandingCmdLists(); // 通知RHI刷新完成. if (GDynamicRHI) { GDynamicRHI->RHIPerFrameRHIFlushComplete(); } // 刪除匿名函數. auto Delete = [](TArray<FRHIResource*>& ToDelete) { for (int32 Index = 0; Index < ToDelete.Num(); Index++) { FRHIResource* Ref = ToDelete[Index]; check(Ref->MarkedForDelete == 1); if (Ref->GetRefCount() == 0) // caches can bring dead objects back to life { CurrentlyDeleting = Ref; delete Ref; CurrentlyDeleting = nullptr; } else { Ref->MarkedForDelete = 0; FPlatformMisc::MemoryBarrier(); } } }; while (1) { if (PendingDeletes.IsEmpty()) { break; } // 平台需要額外的刪除延遲. if (PlatformNeedsExtraDeletionLatency()) { const int32 Index = DeferredDeletionQueue.AddDefaulted(); // 加入延遲刪除隊列DeferredDeletionQueue. ResourcesToDelete& ResourceBatch = DeferredDeletionQueue[Index]; ResourceBatch.FrameDeleted = CurrentFrame; PendingDeletes.PopAll(ResourceBatch.Resources); } // 不需要額外的延遲, 刪除整個列表. else { TArray<FRHIResource*> ToDelete; PendingDeletes.PopAll(ToDelete); Delete(ToDelete); } } const uint32 NumFramesToExpire = RHIRESOURCE_NUM_FRAMES_TO_EXPIRE; // 為3 // 刪除DeferredDeletionQueue. if (DeferredDeletionQueue.Num()) { // 清空整個DeferredDeletionQueue隊列. if (bFlushDeferredDeletes) { FRHICommandListExecutor::GetImmediateCommandList().BlockUntilGPUIdle(); for (int32 Idx = 0; Idx < DeferredDeletionQueue.Num(); ++Idx) { ResourcesToDelete& ResourceBatch = DeferredDeletionQueue[Idx]; Delete(ResourceBatch.Resources); } DeferredDeletionQueue.Empty(); } // 刪除過期的資源列表. else { int32 DeletedBatchCount = 0; while (DeletedBatchCount < DeferredDeletionQueue.Num()) { ResourcesToDelete& ResourceBatch = DeferredDeletionQueue[DeletedBatchCount]; if (((ResourceBatch.FrameDeleted + NumFramesToExpire) < CurrentFrame) || !GIsRHIInitialized) { Delete(ResourceBatch.Resources); ++DeletedBatchCount; } else { break; } } if (DeletedBatchCount) { DeferredDeletionQueue.RemoveAt(0, DeletedBatchCount); } } ++CurrentFrame; } } static bool PlatformNeedsExtraDeletionLatency(); // 平台需要額外的刪除延遲 static bool Bypass(); void DoNoDeferDelete(); // 瞬時資源追蹤. void SetCommitted(bool bInCommitted); bool IsCommitted() const; bool IsValid() const; private: // 運行時標記的數據. mutable FThreadSafeCounter NumRefs; mutable int32 MarkedForDelete; bool bDoNotDeferDelete; bool bCommitted; // 待刪除資源列表, 注意是無鎖無序的指針列表 static TLockFreePointerListUnordered<FRHIResource, PLATFORM_CACHE_LINE_SIZE> PendingDeletes; // 當前正在刪除的資源. static FRHIResource* CurrentlyDeleting; bool DeferDelete() const { // 啟用了多線程渲染且GRHINeedsExtraDeletionLatency為true, 且資源沒有不延遲刪除的標記 return !bDoNotDeferDelete && (GRHINeedsExtraDeletionLatency || !Bypass()); } // 有些api不做內部引用計數,所以必須在刪除資源之前等待額外的幾幀,以確保GPU完全完成它們. 可避免昂貴的柵欄等. struct ResourcesToDelete // 待刪除資源列表 { TArray<FRHIResource*> Resources; // 待刪除的資源. uint32 FrameDeleted; // 等待的幀數. // ...... }; // 延遲刪除的資源隊列. static TArray<ResourcesToDelete> DeferredDeletionQueue; static uint32 CurrentFrame; };
基本上在圖形API中熟悉的任何資源(如:頂點緩沖區,索引緩沖區,混合狀態等)都具有對應FRHIResource子類。
FRHIResource子類眾多,這些子類提供了對圖形API直接操作的GPU資源的抽象。繼承體系如下:
FRHIResource
FRHISamplerState // 采樣器狀態,描述對紋理的采樣方式 AddressU(Wrap、Clamp、Mirror、Border)、AddressV、AddressW、BorderColor、Filter(Point|Bilinear|Trilinear|AnisotropicPoint|AnisotropicLinear)
MaxAnisotropy、MaxLOD、MinLOD、MipLODBias、ComparisonFunc 參考:D3D11_SAMPLER_DESC
FRHIRasterizerState // 光柵化器狀態 FillMode(WIREFRAME|SOLID)、CullMode(NONE|FRONT|BACK)等 參考:D3D11_RASTERIZER_DESC OpenGL參考:glPolygonMode
FRHIDepthStencilState // 深度緩沖區、模板緩沖區狀態 參考:D3D11_DEPTH_STENCIL_DESC
FRHIBlendState // 混合狀態,描述當前DrawCall如何與當前RT進行顏色混合 RT0ColorWriteMask(RGBA等),RT0ColorBlendOp(Add、Subtract、Min、Max、ReverseSubtract)等 參考:D3D11_BLEND_DESC
FRHIVertexDeclaration // 輸入布局(Input Layout)的描述,當確定頂點格式之后,需要描述頂點中各個分量的含義, 這樣GPU才能理解傳給它的數據,最大支持16個分量(如:POSITION、NORMAL、COLOR、TEXCOORD等) 參考:D3D11_INPUT_ELEMENT_DESC
FRHIShader
FRHIGraphicsShader // 渲染管線上的Shader
FRHIVertexShader // 頂點Shader
FRHIHullShader // 曲面細分Shader:外殼着色器 負責把控后續階段的初始化操作,例如細化程度
FRHIDomainShader // 曲面細分Shader:域着色器 對新創建出來的頂點進行處理
FRHIPixelShader // 像素Shader
FRHIGeometryShader // 幾何Shader
FRHIComputeShader // 計算Shader
FRHIRayTracingShader // 光線追蹤Shader
FRHIComputeFence // 計算柵欄 用於CPU與GPU之間或GPU與GPU之間的同步 如:通過Fence等待某一個GPU Queue中的所有Command執行完成
FRHIBoundShaderState // 渲染物體所需的VertexDeclaration、VertexShader、PixelShader、HullShader、DomainShader、GeometryShader的組合
FRHIUniformBuffer // Uniform Buffer,用於從c++中傳遞只讀數據給VS和PS,在D3D11中為Constant Buffer(常量緩沖區) 參考:D3D11_BUFFER_DESC
FRHIVertexBuffer // Vertex Buffer, 描述1個Mesh網格的所有頂點
// TArray<FVector> Vertices = {FVector(0,0,0), FVector(10, 0, 0), FVector(0, 10, 0), FVector(0, 0, 10)};
// FRHIResourceCreateInfo CreateInfo;
// FVertexBufferRHIRef VertexBufferRHI = RHICreateVertexBuffer(Vertices.Num() * sizeof(FVector), BUF_Static | BUF_ShaderResource, CreateInfo);
FRHIIndexBuffer // Index Buffer,Index為Vertex數組中的索引值,3個Index構成1個三角形,1個Index Buffer描述1個Mesh網格
// TArray<int32> Indices = {0, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 1};
// FRHIResourceCreateInfo CreateInfo;
// FIndexBufferRHIRef IndexBufferRHI = RHICreateIndexBuffer(sizeof(int32), Indices.Num() * sizeof(int32), BUF_Static | BUF_ShaderResource, CreateInfo);
FRHIStructuredBuffer // Structured Buffer,具體分為只讀和可讀寫2種。可綁定只讀的Structured Buffer來得到SRV(shader resource views,着色器資源視圖);可綁定可讀寫的Structured Buffer來得到UAV(unordered access view,亂序訪問視圖)
// FRHIResourceCreateInfo CreateInfo;
// FStructuredBufferRHIRef PrimitiveSceneDataBufferRHI = RHICreateStructuredBuffer(sizeof(FVector4), FPrimitiveSceneShaderData::PrimitiveDataStrideInFloat4s * sizeof(FVector4), BUF_Static | BUF_ShaderResource, CreateInfo);
FRHITexture
FRHITexture2D // 2D紋理
// FRHIResourceCreateInfo CreateInfo;
// FTexture2DRHIRef PrimitiveSceneDataTextureRHI = RHICreateTexture2D(FPrimitiveSceneShaderData::PrimitiveDataStrideInFloat4s, 1, PF_A32B32G32R32F, 1, 1, TexCreate_ShaderResource | TexCreate_UAV, CreateInfo);
FRHITexture2DArray // 2D紋理數組
// FRHIResourceCreateInfo CreateInfo;
// FTexture2DArrayRHIRef GridTexture = RHICreateTexture2DArray(NumX, NumY, NumAttributes, PixelFormat, 1, 1, TexCreate_ShaderResource | TexCreate_UAV, CreateInfo);
FRHITexture3D // 3D紋理
// FRHIResourceCreateInfo CreateInfo;
// FTexture3DRHIRef Texture = RHICreateTexture3D(Width, Height, Depth, Format, NumMips, TexCreate_UAV | TexCreate_ShaderResource, CreateInfo);
FRHITextureCube // 立方體紋理(6面)
// FRHIResourceCreateInfo CreateInfo(TEXT("SolidColorCube"));
// FTextureCubeRHIRef TextureCube = RHICreateTextureCube(1, PixelFormat, 1, TexCreate_ShaderResource, CreateInfo);
FRHITextureReference // 紋理引用對象 用於引用某個紋理
FRHIRenderQuery // 渲染查詢
FRHIRenderQueryPool // 渲染查詢池
FDefaultRHIRenderQueryPool
FRHITimestampCalibrationQuery // 時間戳校准查詢
FRHIGPUFence // GPU柵欄類
FGenericRHIGPUFence
FRHIViewport // 視口
FRHIUnorderedAccessView // UAV(unordered access view,亂序訪問視圖) 可綁定可讀寫的Vertex Buffer、Index Buffer、Structured Buffer和紋理
FRHIShaderResourceView // SRV(shader resource views,着色器資源視圖) 可綁定只讀的Vertex Buffer、Index Buffer、Structured Buffer和紋理
FRHIGraphicsPipelineState // 渲染管線狀態
FRHIGraphicsPipelineStateFallBack
FRHIComputePipelineState // Compute Shader管線狀態
FRHIComputePipelineStateFallback
FRHIRayTracingPipelineState // 光線追蹤管線狀態
FRHIRayTracingGeometry
FRHIRayTracingScene
FRHIStagingBuffer // FRHIGPUMemoryReadback使用的通用分段緩沖類
FGenericRHIStagingBuffer
FRHICustomPresent // 自定義呈現
FRHIShaderLibrary
FShaderCodeArchive
FRHIPipelineBinaryLibrary
FNiagaraRHIUniformBufferLayout
這些子類的實現具體可分為:狀態塊、着色器綁定、着色器、管線狀態、緩沖區、紋理、視圖以及其它雜項。
// Engine\Source\Runtime\RHI\Public\RHIResources.h // 狀態塊(State blocks)資源 class FRHISamplerState : public FRHIResource { public: virtual bool IsImmutable() const { return false; } }; class FRHIRasterizerState : public FRHIResource { public: virtual bool GetInitializer(struct FRasterizerStateInitializerRHI& Init) { return false; } }; class FRHIDepthStencilState : public FRHIResource { public: virtual bool GetInitializer(struct FDepthStencilStateInitializerRHI& Init) { return false; } }; class FRHIBlendState : public FRHIResource { public: virtual bool GetInitializer(class FBlendStateInitializerRHI& Init) { return false; } }; // 着色器綁定資源. typedef TArray<struct FVertexElement,TFixedAllocator<MaxVertexElementCount> > FVertexDeclarationElementList; class FRHIVertexDeclaration : public FRHIResource { public: virtual bool GetInitializer(FVertexDeclarationElementList& Init) { return false; } }; class FRHIBoundShaderState : public FRHIResource {}; // 着色器 class FRHIShader : public FRHIResource { public: void SetHash(FSHAHash InHash); FSHAHash GetHash() const; explicit FRHIShader(EShaderFrequency InFrequency); inline EShaderFrequency GetFrequency() const; private: FSHAHash Hash; EShaderFrequency Frequency; }; class FRHIGraphicsShader : public FRHIShader { public: explicit FRHIGraphicsShader(EShaderFrequency InFrequency) : FRHIShader(InFrequency) {} }; class FRHIVertexShader : public FRHIGraphicsShader { public: FRHIVertexShader() : FRHIGraphicsShader(SF_Vertex) {} }; class FRHIHullShader : public FRHIGraphicsShader { public: FRHIHullShader() : FRHIGraphicsShader(SF_Hull) {} }; class FRHIDomainShader : public FRHIGraphicsShader { public: FRHIDomainShader() : FRHIGraphicsShader(SF_Domain) {} }; class FRHIPixelShader : public FRHIGraphicsShader { public: FRHIPixelShader() : FRHIGraphicsShader(SF_Pixel) {} }; class FRHIGeometryShader : public FRHIGraphicsShader { public: FRHIGeometryShader() : FRHIGraphicsShader(SF_Geometry) {} }; class RHI_API FRHIComputeShader : public FRHIShader { public: FRHIComputeShader() : FRHIShader(SF_Compute), Stats(nullptr) {} inline void SetStats(struct FPipelineStateStats* Ptr) { Stats = Ptr; } void UpdateStats(); private: struct FPipelineStateStats* Stats; }; // 管線狀態 class FRHIGraphicsPipelineState : public FRHIResource {}; class FRHIComputePipelineState : public FRHIResource {}; class FRHIRayTracingPipelineState : public FRHIResource {}; // 緩沖區. class FRHIUniformBuffer : public FRHIResource { public: FRHIUniformBuffer(const FRHIUniformBufferLayout& InLayout); FORCEINLINE_DEBUGGABLE uint32 AddRef() const; FORCEINLINE_DEBUGGABLE uint32 Release() const; uint32 GetSize() const; const FRHIUniformBufferLayout& GetLayout() const; bool HasStaticSlot() const; private: const FRHIUniformBufferLayout* Layout; uint32 LayoutConstantBufferSize; }; class FRHIIndexBuffer : public FRHIResource { public: FRHIIndexBuffer(uint32 InStride,uint32 InSize,uint32 InUsage); uint32 GetStride() const; uint32 GetSize() const; uint32 GetUsage() const; protected: FRHIIndexBuffer(); void Swap(FRHIIndexBuffer& Other); void ReleaseUnderlyingResource(); private: uint32 Stride; uint32 Size; uint32 Usage; }; class FRHIVertexBuffer : public FRHIResource { public: FRHIVertexBuffer(uint32 InSize,uint32 InUsage) uint32 GetSize() const; uint32 GetUsage() const; protected: FRHIVertexBuffer(); void Swap(FRHIVertexBuffer& Other); void ReleaseUnderlyingResource(); private: uint32 Size; // e.g. BUF_UnorderedAccess uint32 Usage; }; class FRHIStructuredBuffer : public FRHIResource { public: FRHIStructuredBuffer(uint32 InStride,uint32 InSize,uint32 InUsage) uint32 GetStride() const; uint32 GetSize() const; uint32 GetUsage() const; private: uint32 Stride; uint32 Size; uint32 Usage; }; // 紋理 class FRHITexture : public FRHIResource { public: FRHITexture(uint32 InNumMips, uint32 InNumSamples, EPixelFormat InFormat, uint32 InFlags, FLastRenderTimeContainer* InLastRenderTime, const FClearValueBinding& InClearValue); // 動態類型轉換接口. virtual class FRHITexture2D* GetTexture2D(); virtual class FRHITexture2DArray* GetTexture2DArray(); virtual class FRHITexture3D* GetTexture3D(); virtual class FRHITextureCube* GetTextureCube(); virtual class FRHITextureReference* GetTextureReference(); virtual FIntVector GetSizeXYZ() const = 0; // 獲取平台相關的原生資源指針. virtual void* GetNativeResource() const; virtual void* GetNativeShaderResourceView() const // 獲取平台相關的RHI紋理基類. virtual void* GetTextureBaseRHI(); // 數據接口. uint32 GetNumMips() const; EPixelFormat GetFormat(); uint32 GetFlags() const; uint32 GetNumSamples() const; bool IsMultisampled() const; bool HasClearValue() const; FLinearColor GetClearColor() const; void GetDepthStencilClearValue(float& OutDepth, uint32& OutStencil) const; float GetDepthClearValue() const; uint32 GetStencilClearValue() const; const FClearValueBinding GetClearBinding() const; virtual void GetWriteMaskProperties(void*& OutData, uint32& OutSize); (......) // RHI資源信息. FRHIResourceInfo ResourceInfo; private: // 紋理數據. FClearValueBinding ClearValue; uint32 NumMips; uint32 NumSamples; EPixelFormat Format; uint32 Flags; FLastRenderTimeContainer& LastRenderTime; FLastRenderTimeContainer DefaultLastRenderTime; FName TextureName; }; // 2D RHI紋理. class FRHITexture2D : public FRHITexture { public: FRHITexture2D(uint32 InSizeX,uint32 InSizeY,uint32 InNumMips,uint32 InNumSamples,EPixelFormat InFormat,uint32 InFlags, const FClearValueBinding& InClearValue); virtual FRHITexture2D* GetTexture2D() { return this; } uint32 GetSizeX() const { return SizeX; } uint32 GetSizeY() const { return SizeY; } inline FIntPoint GetSizeXY() const; virtual FIntVector GetSizeXYZ() const override; private: uint32 SizeX; uint32 SizeY; }; // 2D RHI紋理數組. class FRHITexture2DArray : public FRHITexture2D { public: FRHITexture2DArray(uint32 InSizeX,uint32 InSizeY,uint32 InSizeZ,uint32 InNumMips,uint32 NumSamples, EPixelFormat InFormat,uint32 InFlags, const FClearValueBinding& InClearValue); virtual FRHITexture2DArray* GetTexture2DArray() { return this; } virtual FRHITexture2D* GetTexture2D() { return NULL; } uint32 GetSizeZ() const { return SizeZ; } virtual FIntVector GetSizeXYZ() const final override; private: uint32 SizeZ; }; // 3D RHI紋理. class FRHITexture3D : public FRHITexture { public: FRHITexture3D(uint32 InSizeX,uint32 InSizeY,uint32 InSizeZ,uint32 InNumMips,EPixelFormat InFormat,uint32 InFlags, const FClearValueBinding& InClearValue); virtual FRHITexture3D* GetTexture3D() { return this; } uint32 GetSizeX() const { return SizeX; } uint32 GetSizeY() const { return SizeY; } uint32 GetSizeZ() const { return SizeZ; } virtual FIntVector GetSizeXYZ() const final override; private: uint32 SizeX; uint32 SizeY; uint32 SizeZ; }; // 立方體RHI紋理. class FRHITextureCube : public FRHITexture { public: FRHITextureCube(uint32 InSize,uint32 InNumMips,EPixelFormat InFormat,uint32 InFlags, const FClearValueBinding& InClearValue); virtual FRHITextureCube* GetTextureCube(); uint32 GetSize() const; virtual FIntVector GetSizeXYZ() const final override; private: uint32 Size; }; // 紋理引用. class FRHITextureReference : public FRHITexture { public: explicit FRHITextureReference(FLastRenderTimeContainer* InLastRenderTime); virtual FRHITextureReference* GetTextureReference() override { return this; } inline FRHITexture* GetReferencedTexture() const; // 設置引用的紋理 void SetReferencedTexture(FRHITexture* InTexture); virtual FIntVector GetSizeXYZ() const final override; private: // 被引用的紋理資源. TRefCountPtr<FRHITexture> ReferencedTexture; }; class FRHITextureReferenceNullImpl : public FRHITextureReference { public: FRHITextureReferenceNullImpl(); void SetReferencedTexture(FRHITexture* InTexture) { FRHITextureReference::SetReferencedTexture(InTexture); } }; // 雜項資源. // 時間戳校准查詢. class FRHITimestampCalibrationQuery : public FRHIResource { public: uint64 GPUMicroseconds = 0; uint64 CPUMicroseconds = 0; }; // GPU柵欄類. 粒度因RHI而異,即它可能只表示命令緩沖區粒度. RHI的特殊圍欄由此派生而來,實現了真正的GPU->CPU柵欄. // 默認實現總是為輪詢(Poll)返回false,直到插入柵欄的下一幀,因為不是所有api都有GPU/CPU同步對象,需要偽造它。 class FRHIGPUFence : public FRHIResource { public: FRHIGPUFence(FName InName) : FenceName(InName) {} virtual ~FRHIGPUFence() {} virtual void Clear() = 0; // 輪詢圍欄,看看GPU是否已經發出信號. 如果是, 則返回true. virtual bool Poll() const = 0; // 輪詢GPU的子集. virtual bool Poll(FRHIGPUMask GPUMask) const { return Poll(); } // 等待寫入命令的數量. FThreadSafeCounter NumPendingWriteCommands; protected: FName FenceName; }; // 通用的FRHIGPUFence實現. class RHI_API FGenericRHIGPUFence : public FRHIGPUFence { public: FGenericRHIGPUFence(FName InName); virtual void Clear() final override; virtual bool Poll() const final override; void WriteInternal(); private: uint32 InsertedFrameNumber; }; // 渲染查詢. class FRHIRenderQuery : public FRHIResource { }; // 池化的渲染查詢. class RHI_API FRHIPooledRenderQuery { TRefCountPtr<FRHIRenderQuery> Query; FRHIRenderQueryPool* QueryPool = nullptr; public: bool IsValid() const; FRHIRenderQuery* GetQuery() const; void ReleaseQuery(); (.....) }; // 渲染查詢池. class FRHIRenderQueryPool : public FRHIResource { public: virtual ~FRHIRenderQueryPool() {}; virtual FRHIPooledRenderQuery AllocateQuery() = 0; private: friend class FRHIPooledRenderQuery; virtual void ReleaseQuery(TRefCountPtr<FRHIRenderQuery>&& Query) = 0; }; // 計算柵欄. class FRHIComputeFence : public FRHIResource { public: FRHIComputeFence(FName InName); FORCEINLINE bool GetWriteEnqueued() const; virtual void Reset(); virtual void WriteFence(); private: // 自創建以來,標記標簽是否被寫入. 在命令創建時,當隊列等待捕獲CPU上的GPU掛起時,檢查這個標記. bool bWriteEnqueued; }; // 視口. class FRHIViewport : public FRHIResource { public: // 獲取平台相關的原生交換鏈. virtual void* GetNativeSwapChain() const { return nullptr; } // 獲取原生的BackBuffer紋理. virtual void* GetNativeBackBufferTexture() const { return nullptr; } // 獲取原生的BackBuffer渲染紋理. virtual void* GetNativeBackBufferRT() const { return nullptr; } // 獲取原生的窗口. virtual void* GetNativeWindow(void** AddParam = nullptr) const { return nullptr; } // 在視口上設置FRHICustomPresent的handler. virtual void SetCustomPresent(class FRHICustomPresent*) {} virtual class FRHICustomPresent* GetCustomPresent() const { return nullptr; } // 在游戲線程幀更新視口. virtual void Tick(float DeltaTime) {} }; // 視圖: UAV/SRV class FRHIUnorderedAccessView : public FRHIResource {}; class FRHIShaderResourceView : public FRHIResource {}; // 各種RHI資源引用類型定義. 不需要直接引用和管理FRHIResource的實例和計數,而是結合TRefCountPtr的模板類實現自動化管理RHI資源 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; // FRHIGPUMemoryReadback使用的通用分段緩沖類. class FRHIStagingBuffer : public FRHIResource { public: FRHIStagingBuffer(); virtual ~FRHIStagingBuffer(); virtual void *Lock(uint32 Offset, uint32 NumBytes) = 0; virtual void Unlock() = 0; protected: bool bIsLocked; }; class FGenericRHIStagingBuffer : public FRHIStagingBuffer { public: FGenericRHIStagingBuffer(); ~FGenericRHIStagingBuffer(); virtual void* Lock(uint32 Offset, uint32 NumBytes) final override; virtual void Unlock() final override; FVertexBufferRHIRef ShadowBuffer; uint32 Offset; }; // 自定義呈現. class FRHICustomPresent : public FRHIResource { public: FRHICustomPresent() {} virtual ~FRHICustomPresent() {} // 視口尺寸改變時的調用. virtual void OnBackBufferResize() = 0; // 從渲染線程中調用,以查看是否會請求一個原生呈現。 virtual bool NeedsNativePresent() = 0; // RHI線程調用, 執行自定義呈現. virtual bool Present(int32& InOutSyncInterval) = 0; // RHI線程調用, 在Present之后調用. virtual void PostPresent() {}; // 當渲染線程被捕獲時調用. virtual void OnAcquireThreadOwnership() {} // 當渲染線程被釋放時調用. virtual void OnReleaseThreadOwnership() {} };
需要注意的是,以上只是顯示了平台無關的基礎類型,實際上,在不同的圖形API中,會繼承上面的類型。以FRHIUniformBuffer為例,它的繼承體系如下:
以上顯示出FRHIUniformBuffer在D3D11、D3D12、OpenGL、Vulkan、Metal等圖形API的子類,以便實現統一緩沖區的平台相關的資源和操作接口,還有一個特殊的空實現FEmptyUniformBuffer。
與FRHIUniformBuffer類似的是,FRHIResource的其它直接或間接子類也需要被具體的圖形API或操作系統子類實現,以支持在該平台的渲染。下面繪制出最復雜的紋理資源類繼承體系UML圖:
注:上圖做了簡化,除了FRHITexture2D會被各個圖形API繼承子類,其它紋理類型(如FRHITexture2DArray、FRHITexture3D、FRHITextureCube、FRHITextureReference)也會被各個平台繼承並實現。
RHI資源釋放
FRHIResource自身擁有引用計數,提供了引用計數增加AddRef()、減少Release()的接口
然而,我們不需要直接引用和管理FRHIResource的實例和計數,而是結合TRefCountPtr的模板類實現自動化管理RHI資源
typedef TRefCountPtr<FRHIVertexBuffer> FVertexBufferRHIRef;
使用以上類型之后,RHI資源由TRefCountPtr自動管理引用計數,當RHI資源進行構造、拷貝構造、賦值、析構等會自動對引用計數進行增加和刪除
當引用計數為0時,在這些函數中會調用FRHIResource::Release來發起資源的釋放,最后調用RHI資源的析構函數完成資源的釋放工作
比較兩個TRefCountPtr<Type>對象是否相等,由於TRefCountPtr<Type>重載了==運算符,實際比較的是TRefCountPtr<Type>中包裹的Type*指針是否相當
// UnrealEngine\Engine\Source\Runtime\Core\Public\Templates\RefCounting.h template<typename ReferencedType> FORCEINLINE bool operator==(const TRefCountPtr<ReferencedType>& A, const TRefCountPtr<ReferencedType>& B) { return A.GetReference() == B.GetReference(); } template<typename ReferencedType> FORCEINLINE bool operator==(const TRefCountPtr<ReferencedType>& A, ReferencedType* B) { return A.GetReference() == B; } template<typename ReferencedType> FORCEINLINE bool operator==(ReferencedType* A, const TRefCountPtr<ReferencedType>& B) { return A == B.GetReference(); }
更多TRefCountPtr的代碼詳見:UnrealEngine\Engine\Source\Runtime\Core\Public\Templates\RefCounting.h
需要注意的是,FRHIResource的析構函數並沒有釋放任何RHI資源,通常需要在FRHIResource的圖形平台相關的子類析構函數中執行,以FD3D11UniformBuffer為例
// Engine\Source\Runtime\Windows\D3D11RHI\Public\D3D11Resources.h class FD3D11UniformBuffer : public FRHIUniformBuffer { public: // D3D11固定緩沖資源. TRefCountPtr<ID3D11Buffer> Resource; // 包含了RHI引用的資源表. TArray<TRefCountPtr<FRHIResource> > ResourceTable; FD3D11UniformBuffer(class FD3D11DynamicRHI* InD3D11RHI, const FRHIUniformBufferLayout& InLayout, ID3D11Buffer* InResource,const FRingAllocation& InRingAllocation); virtual ~FD3D11UniformBuffer(); (......) }; // Engine\Source\Runtime\Windows\D3D11RHI\Private\D3D11UniformBuffer.cpp FD3D11UniformBuffer::~FD3D11UniformBuffer() { if (!RingAllocation.IsValid() && Resource != nullptr) { D3D11_BUFFER_DESC Desc; Resource->GetDesc(&Desc); // 將此統一緩沖區返回給空閑池. if (Desc.CPUAccessFlags == D3D11_CPU_ACCESS_WRITE && Desc.Usage == D3D11_USAGE_DYNAMIC) { FPooledUniformBuffer NewEntry; NewEntry.Buffer = Resource; NewEntry.FrameFreed = GFrameNumberRenderThread; NewEntry.CreatedSize = Desc.ByteWidth; // Add to this frame's array of free uniform buffers const int32 SafeFrameIndex = (GFrameNumberRenderThread - 1) % NumSafeFrames; const uint32 BucketIndex = GetPoolBucketIndex(Desc.ByteWidth); int32 LastNum = SafeUniformBufferPools[SafeFrameIndex][BucketIndex].Num(); SafeUniformBufferPools[SafeFrameIndex][BucketIndex].Add(NewEntry); FPlatformMisc::MemoryBarrier(); // check for unwanted concurrency } } }
由於FRHIResource::Release會延遲釋放RHI資源,可調用FlushPendingDeletes接口來強行阻塞釋放所有處於Pending Delete狀態的RHI資源,涉及它的調用有
// Engine\Source\Runtime\RenderCore\Private\RenderingThread.cpp void FlushPendingDeleteRHIResources_RenderThread() { if (!IsRunningRHIInSeparateThread()) { FRHIResource::FlushPendingDeletes(); } } // Engine\Source\Runtime\RHI\Private\RHICommandList.cpp void FRHICommandListExecutor::LatchBypass() { #if CAN_TOGGLE_COMMAND_LIST_BYPASS if (IsRunningRHIInSeparateThread()) { (......) } else { (......) if (NewBypass && !bLatchedBypass) { FRHIResource::FlushPendingDeletes(); } } #endif (......) } // Engine\Source\Runtime\RHI\Public\RHICommandList.inl void FRHICommandListImmediate::ImmediateFlush(EImmediateFlushType::Type FlushType) { switch (FlushType) { (......) case EImmediateFlushType::FlushRHIThreadFlushResources: case EImmediateFlushType::FlushRHIThreadFlushResourcesFlushDeferredDeletes: { (......) PipelineStateCache::FlushResources(); FRHIResource::FlushPendingDeletes(FlushType == EImmediateFlushType::FlushRHIThreadFlushResourcesFlushDeferredDeletes); } break; (......) } }
RHI抽象層主要是以上幾處調用FlushPendingDeletes,但以下的圖形平台相關的接口也會調用
FD3D12Adapter::Cleanup()
FD3D12Device::Cleanup()
FVulkanDevice::Destroy()
FVulkanDynamicRHI::Shutdown()
FD3D11DynamicRHI::CleanupD3DDevice()
FRenderResource
封裝FRHIResource,便於渲染線程將游戲線程的數據和操作傳遞到RHI線程(或模塊)中
基類FRenderResource定義了一組渲染資源的行為,是渲染線程的渲染資源代表,由渲染線程管理和傳遞,介於游戲線程和RHI線程的中間數據。
// Engine\Source\Runtime\RenderCore\Public\RenderResource.h class RENDERCORE_API FRenderResource { public: // 遍歷所有資源, 執行回調接口. template<typename FunctionType> static void ForAllResources(const FunctionType& Function); static void InitRHIForAllResources(); static void ReleaseRHIForAllResources(); static void ChangeFeatureLevel(ERHIFeatureLevel::Type NewFeatureLevel); FRenderResource(); FRenderResource(ERHIFeatureLevel::Type InFeatureLevel); virtual ~FRenderResource(); // ----- 以下接口只能在渲染線程調用 ----- // 初始化此資源的動態RHI資源和(或)RHI渲染目標紋理. virtual void InitDynamicRHI() {} // 釋放此資源的動態RHI資源和(或)RHI渲染目標紋理. virtual void ReleaseDynamicRHI() {} // 初始化此資源使用的RHI資源. virtual void InitRHI() {} // 釋放此資源使用的RHI資源. virtual void ReleaseRHI() {} // 初始化資源. virtual void InitResource(); // 釋放資源. virtual void ReleaseResource(); // 如果RHI資源已被初始化, 會被釋放並重新初始化. void UpdateRHI(); // ------------------------------------------ virtual FString GetFriendlyName() const { return TEXT("undefined"); } FORCEINLINE bool IsInitialized() const { return ListIndex != INDEX_NONE; } static void InitPreRHIResources(); private: // 全局資源列表(靜態). static TArray<FRenderResource*>& GetResourceList(); static FThreadSafeCounter ResourceListIterationActive; int32 ListIndex; TEnumAsByte<ERHIFeatureLevel::Type> FeatureLevel; (......) };
下面是游戲線程向渲染線程發送操作FRenderResource的接口
// 初始化/更新/釋放資源. extern RENDERCORE_API void BeginInitResource(FRenderResource* Resource); extern RENDERCORE_API void BeginUpdateResourceRHI(FRenderResource* Resource); extern RENDERCORE_API void BeginReleaseResource(FRenderResource* Resource); extern RENDERCORE_API void StartBatchedRelease(); extern RENDERCORE_API void EndBatchedRelease(); extern RENDERCORE_API void ReleaseResourceAndFlush(FRenderResource* Resource);
FRenderResource子類眾多,下面是部分子類的定義:
// Engine\Source\Runtime\RenderCore\Public\RenderResource.h // 紋理資源. class FTexture : public FRenderResource { public: FTextureRHIRef TextureRHI; // 紋理的RHI資源. FSamplerStateRHIRef SamplerStateRHI; // 紋理的采樣器RHI資源. FSamplerStateRHIRef DeferredPassSamplerStateRHI; // 延遲通道采樣器RHI資源. mutable double LastRenderTime; // 上次渲染的時間. FMipBiasFade MipBiasFade; // 淡入/淡出的Mip偏移值. bool bGreyScaleFormat; // 灰度圖. bool bIgnoreGammaConversions; // 是否忽略Gamma轉換. bool bSRGB; // 是否sRGB空間的顏色. virtual uint32 GetSizeX() const; virtual uint32 GetSizeY() const; virtual uint32 GetSizeZ() const; // 釋放資源. virtual void ReleaseRHI() override { TextureRHI.SafeRelease(); SamplerStateRHI.SafeRelease(); DeferredPassSamplerStateRHI.SafeRelease(); } virtual FString GetFriendlyName() const override { return TEXT("FTexture"); } (......) protected: RENDERCORE_API static FRHISamplerState* GetOrCreateSamplerState(const FSamplerStateInitializerRHI& Initializer); }; // 包含了SRV/UAV的紋理資源. class FTextureWithSRV : public FTexture { public: // 訪問整張紋理的SRV. FShaderResourceViewRHIRef ShaderResourceViewRHI; // 訪問整張紋理的UAV. FUnorderedAccessViewRHIRef UnorderedAccessViewRHI; virtual void ReleaseRHI() override; }; // 持有RHI紋理資源引用的渲染資源. class RENDERCORE_API FTextureReference : public FRenderResource { public: // 紋理的RHI資源引用. FTextureReferenceRHIRef TextureReferenceRHI; // FRenderResource interface. virtual void InitRHI(); virtual void ReleaseRHI(); (......) }; class RENDERCORE_API FVertexBuffer : public FRenderResource { public: // 頂點緩沖的RHI資源引用. FVertexBufferRHIRef VertexBufferRHI; virtual void ReleaseRHI() override; (......); }; class RENDERCORE_API FVertexBufferWithSRV : public FVertexBuffer { public: // 訪問整個緩沖區的SRV/UAV. FShaderResourceViewRHIRef ShaderResourceViewRHI; FUnorderedAccessViewRHIRef UnorderedAccessViewRHI; (......) }; // 索引緩沖. class FIndexBuffer : public FRenderResource { public: // 索引緩沖對應的RHI資源. FIndexBufferRHIRef IndexBufferRHI; (......) };
以下為一些FRenderResource子類的繼承體系:
FRHIResource與FRenderResource的關系
FRenderResource類型將對應的RHI資源類型封裝起來,以便渲染線程將游戲線程的數據和操作傳遞到RHI線程(或模塊)中。
參考