• 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;
    
            // 删除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;
    };

    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 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资源的析构函数完成资源的释放工作

    需要注意的是,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封装的

  • 相关阅读:
    java基础知识复习
    红黑二叉查找树(原理、实现)
    Django admin
    redis+sentinel 安装与配置
    浅谈saltstack
    python3 通过smtplib模块发送邮件
    django 自定义分页模块
    chouti项目
    Django 进阶篇二
    Django 进阶篇
  • 原文地址:https://www.cnblogs.com/kekec/p/15640740.html
Copyright © 2020-2023  润新知