UE4之RHI资源管理
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线程(或模块)中。
参考