可可西

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继承子类,其它纹理类型(如FRHITexture2DArrayFRHITexture3DFRHITextureCubeFRHITextureReference)也会被各个平台继承并实现。

 

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线程(或模块)中。

 

参考

剖析虚幻渲染体系(10)- RHI

【UE4源代码观察】观察D3D11是如何被RHI封装的

 

posted on 2021-12-05 23:05  可可西  阅读(3832)  评论(0编辑  收藏  举报

导航