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封裝的

 


免責聲明!

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



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