可可西

UE4之RHI图形API封装

RHI全称是Render Hardware Interface(渲染硬件接口),封装了众多图形API(DirectX、OpenGL、Vulkan、Metal)之间的差异

基于D3D11 API设计而成,包含了资源管理(Shader、Texture、VertexBuffer等)和图形API封装(DrawIndexedPrimitive、Clear、SetTexture等)。

对Game和Renderer模块提供了简便且一致的概念、数据、资源和接口,实现一份渲染代码跑在多个平台的目标。

RHI相关的测试代码:UnrealEngine\Engine\Plugins\Tests\RHITests\Source\RHITests

资源管理部分详见:UE4之RHI资源管理。本文重点讲 图形API封装

 

IRHICommandContext

IRHICommandContext是RHI的命令上下文接口类,定义了一组图形API相关的操作。在可以并行处理命令列表的平台上,它是一个单独的对象。它和相关继承类型定义如下:

// Engine\Source\Runtime\RHI\Public\RHIContext.h

// 能够执行计算工作的上下文。可以在gfx管道上执行异步或计算.
class IRHIComputeContext
{
public:
    virtual ~IRHIComputeContext();

    // 设置/派发计算着色器.
    virtual void RHISetComputeShader(FRHIComputeShader* ComputeShader) = 0;
    virtual void RHISetComputePipelineState(FRHIComputePipelineState* ComputePipelineState);
    virtual void RHIDispatchComputeShader(uint32 ThreadGroupCountX, uint32 ThreadGroupCountY, uint32 ThreadGroupCountZ) = 0;
    virtual void RHIDispatchIndirectComputeShader(FRHIVertexBuffer* ArgumentBuffer, uint32 ArgumentOffset) = 0;
    virtual void RHISetAsyncComputeBudget(EAsyncComputeBudget Budget) {}
    
    // 转换资源.
    virtual void RHIBeginTransitions(TArrayView<const FRHITransition*> Transitions) = 0;
    virtual void RHIEndTransitions(TArrayView<const FRHITransition*> Transitions) = 0;

    // UAV
    virtual void RHIClearUAVFloat(FRHIUnorderedAccessView* UnorderedAccessViewRHI, const FVector4& Values) = 0;
    virtual void RHIClearUAVUint(FRHIUnorderedAccessView* UnorderedAccessViewRHI, const FUintVector4& Values) = 0;
    virtual void RHIBeginUAVOverlap() {}
    virtual void RHIEndUAVOverlap() {}
    virtual void RHIBeginUAVOverlap(TArrayView<FRHIUnorderedAccessView* const> UAVs) {}
    virtual void RHIEndUAVOverlap(TArrayView<FRHIUnorderedAccessView* const> UAVs) {}

    // 着色器参数.
    virtual void RHISetShaderTexture(FRHIComputeShader* PixelShader, uint32 TextureIndex, FRHITexture* NewTexture) = 0;
    virtual void RHISetShaderSampler(FRHIComputeShader* ComputeShader, uint32 SamplerIndex, FRHISamplerState* NewState) = 0;
    virtual void RHISetUAVParameter(FRHIComputeShader* ComputeShader, uint32 UAVIndex, FRHIUnorderedAccessView* UAV) = 0;
    virtual void RHISetUAVParameter(FRHIComputeShader* ComputeShader, uint32 UAVIndex, FRHIUnorderedAccessView* UAV, uint32 InitialCount) = 0;
    virtual void RHISetShaderResourceViewParameter(FRHIComputeShader* ComputeShader, uint32 SamplerIndex, FRHIShaderResourceView* SRV) = 0;
    virtual void RHISetShaderUniformBuffer(FRHIComputeShader* ComputeShader, uint32 BufferIndex, FRHIUniformBuffer* Buffer) = 0;
    virtual void RHISetShaderParameter(FRHIComputeShader* ComputeShader, uint32 BufferIndex, uint32 BaseIndex, uint32 NumBytes, const void* NewValue) = 0;
    virtual void RHISetGlobalUniformBuffers(const FUniformBufferStaticBindings& InUniformBuffers);
    
    // 压入/弹出事件.
    virtual void RHIPushEvent(const TCHAR* Name, FColor Color) = 0;
    virtual void RHIPopEvent() = 0;

    // 其它接口.
    virtual void RHISubmitCommandsHint() = 0;
    virtual void RHIInvalidateCachedState() {}
    virtual void RHICopyToStagingBuffer(FRHIVertexBuffer* SourceBufferRHI, FRHIStagingBuffer* DestinationStagingBufferRHI, uint32 InOffset, uint32 InNumBytes);
    virtual void RHIWriteGPUFence(FRHIGPUFence* FenceRHI);
    virtual void RHISetGPUMask(FRHIGPUMask GPUMask);

    // 加速结构.
    virtual void RHIBuildAccelerationStructure(FRHIRayTracingGeometry* Geometry);
    virtual void RHIBuildAccelerationStructures(const TArrayView<const FAccelerationStructureBuildParams> Params);
    virtual void RHIBuildAccelerationStructure(FRHIRayTracingScene* Scene);

    // 获取计算上下文.
    inline IRHIComputeContext& GetLowestLevelContext() { return *this; }
    inline IRHIComputeContext& GetHighestLevelContext() { return *this; }
};

// 命令上下文.
class IRHICommandContext : public IRHIComputeContext
{
public:
    virtual ~IRHICommandContext();

    // 派发计算.
    virtual void RHIDispatchComputeShader(uint32 ThreadGroupCountX, uint32 ThreadGroupCountY, uint32 ThreadGroupCountZ) = 0;
    virtual void RHIDispatchIndirectComputeShader(FRHIVertexBuffer* ArgumentBuffer, uint32 ArgumentOffset) = 0;
    
    // 渲染查询.
    virtual void RHIBeginRenderQuery(FRHIRenderQuery* RenderQuery) = 0;
    virtual void RHIEndRenderQuery(FRHIRenderQuery* RenderQuery) = 0;
    virtual void RHIPollOcclusionQueries();

    // 开启/结束接口.
    virtual void RHIBeginDrawingViewport(FRHIViewport* Viewport, FRHITexture* RenderTargetRHI) = 0;
    virtual void RHIEndDrawingViewport(FRHIViewport* Viewport, bool bPresent, bool bLockToVsync) = 0;
    virtual void RHIBeginFrame() = 0;
    virtual void RHIEndFrame() = 0;
    virtual void RHIBeginScene() = 0;
    virtual void RHIEndScene() = 0;
    virtual void RHIBeginUpdateMultiFrameResource(FRHITexture* Texture);
    virtual void RHIEndUpdateMultiFrameResource(FRHITexture* Texture);
    virtual void RHIBeginUpdateMultiFrameResource(FRHIUnorderedAccessView* UAV);
    virtual void RHIEndUpdateMultiFrameResource(FRHIUnorderedAccessView* UAV);
        
    // 设置数据.
    virtual void RHISetStreamSource(uint32 StreamIndex, FRHIVertexBuffer* VertexBuffer, uint32 Offset) = 0;
    virtual void RHISetViewport(float MinX, float MinY, float MinZ, float MaxX, float MaxY, float MaxZ) = 0;
    virtual void RHISetStereoViewport(...);
    virtual void RHISetScissorRect(bool bEnable, uint32 MinX, uint32 MinY, uint32 MaxX, uint32 MaxY) = 0;
    virtual void RHISetGraphicsPipelineState(FRHIGraphicsPipelineState* GraphicsState, bool bApplyAdditionalState) = 0;

    // 设置着色器参数.
    virtual void RHISetShaderTexture(FRHIGraphicsShader* Shader, uint32 TextureIndex, FRHITexture* NewTexture) = 0;
    virtual void RHISetShaderTexture(FRHIComputeShader* PixelShader, uint32 TextureIndex, FRHITexture* NewTexture) = 0;
    virtual void RHISetShaderSampler(FRHIComputeShader* ComputeShader, uint32 SamplerIndex, FRHISamplerState* NewState) = 0;
    virtual void RHISetShaderSampler(FRHIGraphicsShader* Shader, uint32 SamplerIndex, FRHISamplerState* NewState) = 0;
    virtual void RHISetUAVParameter(FRHIPixelShader* PixelShader, uint32 UAVIndex, FRHIUnorderedAccessView* UAV) = 0;
    virtual void RHISetUAVParameter(FRHIComputeShader* ComputeShader, uint32 UAVIndex, FRHIUnorderedAccessView* UAV) = 0;
    virtual void RHISetUAVParameter(FRHIComputeShader* ComputeShader, uint32 UAVIndex, FRHIUnorderedAccessView* UAV, uint32 InitialCount) = 0;
    virtual void RHISetShaderResourceViewParameter(FRHIComputeShader* ComputeShader, uint32 SamplerIndex, FRHIShaderResourceView* SRV) = 0;
    virtual void RHISetShaderResourceViewParameter(FRHIGraphicsShader* Shader, uint32 SamplerIndex, FRHIShaderResourceView* SRV) = 0;
    virtual void RHISetShaderUniformBuffer(FRHIGraphicsShader* Shader, uint32 BufferIndex, FRHIUniformBuffer* Buffer) = 0;
    virtual void RHISetShaderUniformBuffer(FRHIComputeShader* ComputeShader, uint32 BufferIndex, FRHIUniformBuffer* Buffer) = 0;
    virtual void RHISetShaderParameter(FRHIGraphicsShader* Shader, uint32 BufferIndex, uint32 BaseIndex, uint32 NumBytes, const void* NewValue) = 0;
    virtual void RHISetShaderParameter(FRHIComputeShader* ComputeShader, uint32 BufferIndex, uint32 BaseIndex, uint32 NumBytes, const void* NewValue) = 0;
    virtual void RHISetStencilRef(uint32 StencilRef) {}
    virtual void RHISetBlendFactor(const FLinearColor& BlendFactor) {}
    
    // 绘制图元.
    virtual void RHIDrawPrimitive(uint32 BaseVertexIndex, uint32 NumPrimitives, uint32 NumInstances) = 0;
    virtual void RHIDrawPrimitiveIndirect(FRHIVertexBuffer* ArgumentBuffer, uint32 ArgumentOffset) = 0;
    virtual void RHIDrawIndexedIndirect(FRHIIndexBuffer* IndexBufferRHI, FRHIStructuredBuffer* ArgumentsBufferRHI, int32 DrawArgumentsIndex, uint32 NumInstances) = 0;
    virtual void RHIDrawIndexedPrimitive(FRHIIndexBuffer* IndexBuffer, int32 BaseVertexIndex, uint32 FirstInstance, uint32 NumVertices, uint32 StartIndex, uint32 NumPrimitives, uint32 NumInstances) = 0;
    virtual void RHIDrawIndexedPrimitiveIndirect(FRHIIndexBuffer* IndexBuffer, FRHIVertexBuffer* ArgumentBuffer, uint32 ArgumentOffset) = 0;

    // 其它接口
    virtual void RHISetDepthBounds(float MinDepth, float MaxDepth) = 0;
    virtual void RHISetShadingRate(EVRSShadingRate ShadingRate, EVRSRateCombiner Combiner);
    virtual void RHISetShadingRateImage(FRHITexture* RateImageTexture, EVRSRateCombiner Combiner);
    virtual void RHISetMultipleViewports(uint32 Count, const FViewportBounds* Data) = 0;
    virtual void RHICopyToResolveTarget(FRHITexture* SourceTexture, FRHITexture* DestTexture, const FResolveParams& ResolveParams) = 0;
    virtual void RHIResummarizeHTile(FRHITexture2D* DepthTexture);
    virtual void RHICalibrateTimers();
    virtual void RHICalibrateTimers(FRHITimestampCalibrationQuery* CalibrationQuery);
    virtual void RHIDiscardRenderTargets(bool Depth, bool Stencil, uint32 ColorBitMask) {}
    
    // 纹理
    virtual void RHIUpdateTextureReference(FRHITextureReference* TextureRef, FRHITexture* NewTexture) = 0;
    virtual void RHICopyTexture(FRHITexture* SourceTexture, FRHITexture* DestTexture, const FRHICopyTextureInfo& CopyInfo);
    virtual void RHICopyBufferRegion(FRHIVertexBuffer* DestBuffer, ...);
    
    // Pass相关.
    virtual void RHIBeginRenderPass(const FRHIRenderPassInfo& InInfo, const TCHAR* InName) = 0;
    virtual void RHIEndRenderPass() = 0;
    virtual void RHINextSubpass();

    // 光线追踪.
    virtual void RHIClearRayTracingBindings(FRHIRayTracingScene* Scene);
    virtual void RHIBuildAccelerationStructures(const TArrayView<const FAccelerationStructureBuildParams> Params);
    virtual void RHIBuildAccelerationStructure(FRHIRayTracingGeometry* Geometry) final override;
    virtual void RHIBuildAccelerationStructure(FRHIRayTracingScene* Scene);
    virtual void RHIRayTraceOcclusion(FRHIRayTracingScene* Scene, ...);
    virtual void RHIRayTraceIntersection(FRHIRayTracingScene* Scene, ...);
    virtual void RHIRayTraceDispatch(FRHIRayTracingPipelineState* RayTracingPipelineState, ...);
    virtual void RHISetRayTracingHitGroups(FRHIRayTracingScene* Scene, ...);
    virtual void RHISetRayTracingHitGroup(FRHIRayTracingScene* Scene, ...);
    virtual void RHISetRayTracingCallableShader(FRHIRayTracingScene* Scene, ...);
    virtual void RHISetRayTracingMissShader(FRHIRayTracingScene* Scene, ...);
    
    (......)

protected:
    // 渲染Pass信息.
    FRHIRenderPassInfo RenderPassInfo;
};

IRHICommandContext还有许多子类:

  • IRHICommandContextPSOFallback:不支持真正的图形管道的RHI命令上下文。

    • FNullDynamicRHI:空实现的动态绑定RHI。
    • FOpenGLDynamicRHI:OpenGL的动态RHI。
    • FD3D11DynamicRHI:D3D11的动态RHI。
  • FMetalRHICommandContext:Metal平台的命令上下文。

  • FD3D12CommandContextBase:D3D12的命令上下文。

  • FVulkanCommandListContext:Vulkan平台的命令队列上下文。

  • FEmptyDynamicRHI:动态绑定的RHI实现的接口。

  • FValidationContext:校验上下文。

上述的子类中,平台相关的部分子类还继承了FDynamicRHI。

IRHICommandContextPSOFallback比较特殊,它的子类都是不支持并行绘制的图形API(如:OpenGL、D3D11)。

IRHICommandContextPSOFallback定义如下:

class IRHICommandContextPSOFallback : public IRHICommandContext
{
public:
    // 设置渲染状态.
    virtual void RHISetBoundShaderState(FRHIBoundShaderState* BoundShaderState) = 0;
    virtual void RHISetDepthStencilState(FRHIDepthStencilState* NewState, uint32 StencilRef) = 0;
    virtual void RHISetRasterizerState(FRHIRasterizerState* NewState) = 0;
    virtual void RHISetBlendState(FRHIBlendState* NewState, const FLinearColor& BlendFactor) = 0;
    virtual void RHIEnableDepthBoundsTest(bool bEnable) = 0;
    // 管线状态.
    virtual void RHISetGraphicsPipelineState(FRHIGraphicsPipelineState* GraphicsState, bool bApplyAdditionalState) override;
};

 

IRHICommandContext的核心继承UML图如下:

 

IRHICommandContextContainer

IRHICommandContextContainer就是包含了IRHICommandContext对象的类型,它和核心继承子类的定义如下:

// Engine\Source\Runtime\RHI\Public\RHICommandList.h

class IRHICommandContextContainer
{
public:
    virtual ~IRHICommandContextContainer();

    // 获取IRHICommandContext实例.
    virtual IRHICommandContext* GetContext();
    virtual void SubmitAndFreeContextContainer(int32 Index, int32 Num);
    virtual void FinishContext();
};

// Engine\Source\Runtime\Apple\MetalRHI\Private\MetalContext.cpp

class FMetalCommandContextContainer : public IRHICommandContextContainer
{
    // FMetalRHICommandContext列表的下一个.
    FMetalRHICommandContext* CmdContext;
    int32 Index;
    int32 Num;
    
public:
    void* operator new(size_t Size);
    void operator delete(void *RawMemory);
    
    FMetalCommandContextContainer(int32 InIndex, int32 InNum);
    virtual ~FMetalCommandContextContainer() override final;
    
    virtual IRHICommandContext* GetContext() override final;
    virtual void FinishContext() override final;
    // 提交并释放自己.
    virtual void SubmitAndFreeContextContainer(int32 NewIndex, int32 NewNum) override final;
};

// FMetalCommandContextContainer分配器.
static TLockFreeFixedSizeAllocator<sizeof(FMetalCommandContextContainer), PLATFORM_CACHE_LINE_SIZE, FThreadSafeCounter> FMetalCommandContextContainerAllocator;

// Engine\Source\Runtime\D3D12RHI\Private\D3D12CommandContext.cpp

class FD3D12CommandContextContainer : public IRHICommandContextContainer
{
    // 适配器.
    FD3D12Adapter* Adapter;
    // 命令上下文.
    FD3D12CommandContext* CmdContext;
    // 上下文重定向器.
    FD3D12CommandContextRedirector* CmdContextRedirector;
    FRHIGPUMask GPUMask;

    // 命令队列列表.
    TArray<FD3D12CommandListHandle> CommandLists;

public:
    void* operator new(size_t Size);
    void operator delete(void* RawMemory);

    FD3D12CommandContextContainer(FD3D12Adapter* InAdapter, FRHIGPUMask InGPUMask);
    virtual ~FD3D12CommandContextContainer() override

    virtual IRHICommandContext* GetContext() override;
    virtual void FinishContext() override;
    virtual void SubmitAndFreeContextContainer(int32 Index, int32 Num) override;
};

// Engine\Source\Runtime\VulkanRHI\Private\VulkanContext.h

struct FVulkanCommandContextContainer : public IRHICommandContextContainer, public VulkanRHI::FDeviceChild
{
    // 命令队列上下文.
    FVulkanCommandListContext* CmdContext;

    FVulkanCommandContextContainer(FVulkanDevice* InDevice);

    virtual IRHICommandContext* GetContext() override final;
    virtual void FinishContext() override final;
    virtual void SubmitAndFreeContextContainer(int32 Index, int32 Num) override final;

    void* operator new(size_t Size);
    void operator delete(void* RawMemory);
};

IRHICommandContextContainer相当于存储了一个或一组命令上下文的容器,以支持并行化地提交命令队列,只在D3D12、Metal、Vulkan等现代图形API中有实现。

完整继承UML图如下:

 

 

FDynamicRHI

FDynamicRHI是由动态绑定的RHI实现的接口,它定义的接口和CommandContext比较相似,部分如下:

class RHI_API FDynamicRHI
{
public:
    virtual ~FDynamicRHI() {}

    virtual void Init() = 0;
    virtual void PostInit() {}
    virtual void Shutdown() = 0;

    void InitPixelFormatInfo(const TArray<uint32>& PixelFormatBlockBytesIn);

    // ---- RHI接口 ----

    // 下列接口要求FlushType: Thread safe
    virtual FSamplerStateRHIRef RHICreateSamplerState(const FSamplerStateInitializerRHI& Initializer) = 0;
    virtual FRasterizerStateRHIRef RHICreateRasterizerState(const FRasterizerStateInitializerRHI& Initializer) = 0;
    virtual FDepthStencilStateRHIRef RHICreateDepthStencilState(const FDepthStencilStateInitializerRHI& Initializer) = 0;
    virtual FBlendStateRHIRef RHICreateBlendState(const FBlendStateInitializerRHI& Initializer) = 0;

    // 下列接口要求FlushType: Wait RHI Thread
    virtual FVertexDeclarationRHIRef RHICreateVertexDeclaration(const FVertexDeclarationElementList& Elements) = 0;
    virtual FPixelShaderRHIRef RHICreatePixelShader(TArrayView<const uint8> Code, const FSHAHash& Hash) = 0;
    virtual FVertexShaderRHIRef RHICreateVertexShader(TArrayView<const uint8> Code, const FSHAHash& Hash) = 0;
    virtual FHullShaderRHIRef RHICreateHullShader(TArrayView<const uint8> Code, const FSHAHash& Hash) = 0;
    virtual FDomainShaderRHIRef RHICreateDomainShader(TArrayView<const uint8> Code, const FSHAHash& Hash) = 0;
    virtual FGeometryShaderRHIRef RHICreateGeometryShader(TArrayView<const uint8> Code, const FSHAHash& Hash) = 0;
    virtual FComputeShaderRHIRef RHICreateComputeShader(TArrayView<const uint8> Code, const FSHAHash& Hash) = 0;

     // FlushType: Must be Thread-Safe.
    virtual FRenderQueryPoolRHIRef RHICreateRenderQueryPool(ERenderQueryType QueryType, uint32 NumQueries = UINT32_MAX);
    inline FComputeFenceRHIRef RHICreateComputeFence(const FName& Name);
    
    virtual FGPUFenceRHIRef RHICreateGPUFence(const FName &Name);
    virtual void RHICreateTransition(FRHITransition* Transition, ERHIPipeline SrcPipelines, ERHIPipeline DstPipelines, ERHICreateTransitionFlags CreateFlags, TArrayView<const FRHITransitionInfo> Infos);
    virtual void RHIReleaseTransition(FRHITransition* Transition);

    // FlushType: Thread safe.    
    virtual FStagingBufferRHIRef RHICreateStagingBuffer();
    virtual void* RHILockStagingBuffer(FRHIStagingBuffer* StagingBuffer, FRHIGPUFence* Fence, uint32 Offset, uint32 SizeRHI);
    virtual void RHIUnlockStagingBuffer(FRHIStagingBuffer* StagingBuffer);
    
    // FlushType: Thread safe, but varies depending on the RHI
    virtual FBoundShaderStateRHIRef RHICreateBoundShaderState(FRHIVertexDeclaration* VertexDeclaration, FRHIVertexShader* VertexShader, FRHIHullShader* HullShader, FRHIDomainShader* DomainShader, FRHIPixelShader* PixelShader, FRHIGeometryShader* GeometryShader) = 0;
    // FlushType: Thread safe
    virtual FGraphicsPipelineStateRHIRef RHICreateGraphicsPipelineState(const FGraphicsPipelineStateInitializer& Initializer);
    
    // FlushType: Thread safe, but varies depending on the RHI
    virtual FUniformBufferRHIRef RHICreateUniformBuffer(const void* Contents, const FRHIUniformBufferLayout& Layout, EUniformBufferUsage Usage, EUniformBufferValidation Validation) = 0;
    virtual void RHIUpdateUniformBuffer(FRHIUniformBuffer* UniformBufferRHI, const void* Contents) = 0;
    
    // FlushType: Wait RHI Thread
    virtual FIndexBufferRHIRef RHICreateIndexBuffer(uint32 Stride, uint32 Size, uint32 InUsage, ERHIAccess InResourceState, FRHIResourceCreateInfo& CreateInfo) = 0;
    virtual void* RHILockIndexBuffer(FRHICommandListImmediate& RHICmdList, FRHIIndexBuffer* IndexBuffer, uint32 Offset, uint32 Size, EResourceLockMode LockMode);
    virtual void RHIUnlockIndexBuffer(FRHICommandListImmediate& RHICmdList, FRHIIndexBuffer* IndexBuffer);
    virtual void RHITransferIndexBufferUnderlyingResource(FRHIIndexBuffer* DestIndexBuffer, FRHIIndexBuffer* SrcIndexBuffer);

    // FlushType: Wait RHI Thread
    virtual FVertexBufferRHIRef RHICreateVertexBuffer(uint32 Size, uint32 InUsage, ERHIAccess InResourceState, FRHIResourceCreateInfo& CreateInfo) = 0;
    // FlushType: Flush RHI Thread
    virtual void* RHILockVertexBuffer(FRHICommandListImmediate& RHICmdList, FRHIVertexBuffer* VertexBuffer, uint32 Offset, uint32 SizeRHI, EResourceLockMode LockMode);
    virtual void RHIUnlockVertexBuffer(FRHICommandListImmediate& RHICmdList, FRHIVertexBuffer* VertexBuffer);
    // FlushType: Flush Immediate (seems dangerous)
    virtual void RHICopyVertexBuffer(FRHIVertexBuffer* SourceBuffer, FRHIVertexBuffer* DestBuffer) = 0;
    virtual void RHITransferVertexBufferUnderlyingResource(FRHIVertexBuffer* DestVertexBuffer, FRHIVertexBuffer* SrcVertexBuffer);

    // FlushType: Wait RHI Thread
    virtual FStructuredBufferRHIRef RHICreateStructuredBuffer(uint32 Stride, uint32 Size, uint32 InUsage, ERHIAccess InResourceState, FRHIResourceCreateInfo& CreateInfo) = 0;
    // FlushType: Flush RHI Thread
    virtual void* RHILockStructuredBuffer(FRHICommandListImmediate& RHICmdList, FRHIStructuredBuffer* StructuredBuffer, uint32 Offset, uint32 SizeRHI, EResourceLockMode LockMode);
    virtual void RHIUnlockStructuredBuffer(FRHICommandListImmediate& RHICmdList, FRHIStructuredBuffer* StructuredBuffer);

    // FlushType: Wait RHI Thread
    virtual FUnorderedAccessViewRHIRef RHICreateUnorderedAccessView(FRHIStructuredBuffer* StructuredBuffer, bool bUseUAVCounter, bool bAppendBuffer) = 0;
    // FlushType: Wait RHI Thread
    virtual FUnorderedAccessViewRHIRef RHICreateUnorderedAccessView(FRHITexture* Texture, uint32 MipLevel) = 0;
    // FlushType: Wait RHI Thread
    virtual FUnorderedAccessViewRHIRef RHICreateUnorderedAccessView(FRHITexture* Texture, uint32 MipLevel, uint8 Format);

    (......)

    // RHI帧更新,须从主线程调用,FlushType: Thread safe
    virtual void RHITick(float DeltaTime) = 0;
    // 阻塞CPU直到GPU执行完成变成空闲. FlushType: Flush Immediate (seems wrong)
    virtual void RHIBlockUntilGPUIdle() = 0;
    // 开始当前帧,并确保GPU正在积极地工作 FlushType: Flush Immediate (copied from RHIBlockUntilGPUIdle)
    virtual void RHISubmitCommandsAndFlushGPU() {};

    // 通知RHI准备暂停它.
    virtual void RHIBeginSuspendRendering() {};
    // 暂停RHI渲染并将控制权交给系统的操作, FlushType: Thread safe
    virtual void RHISuspendRendering() {};
    // 继续RHI渲染, FlushType: Thread safe
    virtual void RHIResumeRendering() {};
    // FlushType: Flush Immediate
    virtual bool RHIIsRenderingSuspended() { return false; };

    // FlushType: called from render thread when RHI thread is flushed 
    // 仅在FRHIResource::FlushPendingDeletes内的延迟删除之前每帧调用.
    virtual void RHIPerFrameRHIFlushComplete();

    // 执行命令队列, FlushType: Wait RHI Thread
    virtual void RHIExecuteCommandList(FRHICommandList* CmdList) = 0;

    // FlushType: Flush RHI Thread
    virtual void* RHIGetNativeDevice() = 0;
    // FlushType: Flush RHI Thread
    virtual void* RHIGetNativeInstance() = 0;

    // 获取命令上下文. FlushType: Thread safe
    virtual IRHICommandContext* RHIGetDefaultContext() = 0;
    // 获取计算上下文. FlushType: Thread safe
    virtual IRHIComputeContext* RHIGetDefaultAsyncComputeContext();

    // FlushType: Thread safe
    virtual class IRHICommandContextContainer* RHIGetCommandContextContainer(int32 Index, int32 Num) = 0;

    // 直接由渲染线程调用的接口, 以优化RHI调用.
    virtual FVertexBufferRHIRef CreateAndLockVertexBuffer_RenderThread(class FRHICommandListImmediate& RHICmdList, uint32 Size, uint32 InUsage, ERHIAccess InResourceState, FRHIResourceCreateInfo& CreateInfo, void*& OutDataBuffer);
    virtual FIndexBufferRHIRef CreateAndLockIndexBuffer_RenderThread(class FRHICommandListImmediate& RHICmdList, uint32 Stride, uint32 Size, uint32 InUsage, ERHIAccess InResourceState, FRHIResourceCreateInfo& CreateInfo, void*& OutDataBuffer);
    
    (......)

    // Buffer Lock/Unlock
    virtual void* LockVertexBuffer_BottomOfPipe(class FRHICommandListImmediate& RHICmdList, ...);
    virtual void* LockIndexBuffer_BottomOfPipe(class FRHICommandListImmediate& RHICmdList, ...);
    
    (......)
};

 

以上只显示了部分接口,其中部分接口要求从渲染线程调用,部分须从游戏线程调用。大多数接口在被调用前需刷新指定类型的命令,比如:

class RHI_API FDynamicRHI
{
    // FlushType: Wait RHI Thread
    void RHIExecuteCommandList(FRHICommandList* CmdList);

    // FlushType: Flush Immediate
    void RHIBlockUntilGPUIdle();

    // FlushType: Thread safe 
    void RHITick(float DeltaTime);
};

那么调用以上接口的代码如下:

class RHI_API FRHICommandListImmediate : public FRHICommandList
{
    void ExecuteCommandList(FRHICommandList* CmdList)
    {
        // 等待RHI线程.
        FScopedRHIThreadStaller StallRHIThread(*this);
        GDynamicRHI->RHIExecuteCommandList(CmdList);
    }
    
    void BlockUntilGPUIdle()
    {
        // 调用FDynamicRHI::RHIBlockUntilGPUIdle须刷新RHI.
        ImmediateFlush(EImmediateFlushType::FlushRHIThread);  
        GDynamicRHI->RHIBlockUntilGPUIdle();
    }
    
    void Tick(float DeltaTime)
    {
        // 由于FDynamicRHI::RHITick是Thread Safe(线程安全), 所以不需要调用ImmediateFlush或等待事件.
        GDynamicRHI->RHITick(DeltaTime);
    }
};

 

我们继续看FDynamicRHI的子类定义:

// Engine\Source\Runtime\Apple\MetalRHI\Private\MetalDynamicRHI.h

class FMetalDynamicRHI : public FDynamicRHI
{
public:
    FMetalDynamicRHI(ERHIFeatureLevel::Type RequestedFeatureLevel);
    ~FMetalDynamicRHI();
    
    // 设置必要的内部资源
    void SetupRecursiveResources();

    // FDynamicRHI interface.
    virtual void Init();
    virtual void Shutdown() {}
    virtual const TCHAR* GetName() override { return TEXT("Metal"); }
    
    virtual FSamplerStateRHIRef RHICreateSamplerState(const FSamplerStateInitializerRHI& Initializer) final override;
    virtual FRasterizerStateRHIRef RHICreateRasterizerState(const FRasterizerStateInitializerRHI& Initializer) final override;
    virtual FDepthStencilStateRHIRef RHICreateDepthStencilState(...) final override;
    
    (......)
    
private:
    // 立即模式上下文.
    FMetalRHIImmediateCommandContext ImmediateContext;
    // 异步计算上下文.
    FMetalRHICommandContext* AsyncComputeContext;
    // 顶点声明缓存.
    TMap<uint32, FVertexDeclarationRHIRef> VertexDeclarationCache;
};

// Engine\Source\Runtime\D3D12RHI\Private\D3D12RHIPrivate.h

class FD3D12DynamicRHI : public FDynamicRHI
{
    static FD3D12DynamicRHI* SingleD3DRHI;

public:
    static D3D12RHI_API FD3D12DynamicRHI* GetD3DRHI() { return SingleD3DRHI; }

    FD3D12DynamicRHI(const TArray<TSharedPtr<FD3D12Adapter>>& ChosenAdaptersIn, bool bInPixEventEnabled);
    virtual ~FD3D12DynamicRHI();

    // FDynamicRHI interface.
    virtual void Init() override;
    virtual void PostInit() override;
    virtual void Shutdown() override;
    virtual const TCHAR* GetName() override { return TEXT("D3D12"); }

    virtual FSamplerStateRHIRef RHICreateSamplerState(const FSamplerStateInitializerRHI& Initializer) final override;
    virtual FRasterizerStateRHIRef RHICreateRasterizerState(const FRasterizerStateInitializerRHI& Initializer) final override;
    virtual FDepthStencilStateRHIRef RHICreateDepthStencilState(const FDepthStencilStateInitializerRHI& Initializer) final override;
    
    (......)
    
protected:
    // 已选择的适配器.
    TArray<TSharedPtr<FD3D12Adapter>> ChosenAdapters;
    // AMD AGS工具库上下文.
    AGSContext* AmdAgsContext;

    // D3D12设备.
    inline FD3D12Device* GetRHIDevice(uint32 GPUIndex)
    {
        return GetAdapter().GetDevice(GPUIndex);
    }
    
    (......)
};

// Engine\Source\Runtime\EmptyRHI\Public\EmptyRHI.h

class FEmptyDynamicRHI : public FDynamicRHI, public IRHICommandContext
{
    (......)
};

// Engine\Source\Runtime\NullDrv\Public\NullRHI.h

class FNullDynamicRHI : public FDynamicRHI , public IRHICommandContextPSOFallback
{
    (......)
};


class OPENGLDRV_API FOpenGLDynamicRHI  final : public FDynamicRHI, public IRHICommandContextPSOFallback
{
public:
    FOpenGLDynamicRHI();
    ~FOpenGLDynamicRHI();

    // FDynamicRHI interface.
    virtual void Init();
    virtual void PostInit();

    virtual void Shutdown();
    virtual const TCHAR* GetName() override { return TEXT("OpenGL"); }
    
    virtual FRasterizerStateRHIRef RHICreateRasterizerState(const FRasterizerStateInitializerRHI& Initializer) final override;
    virtual FDepthStencilStateRHIRef RHICreateDepthStencilState(const FDepthStencilStateInitializerRHI& Initializer) final override;
    virtual FBlendStateRHIRef RHICreateBlendState(const FBlendStateInitializerRHI& Initializer) final override;
    
    (......)
    
private:
    // 计数器.
    uint32 SceneFrameCounter;
    uint32 ResourceTableFrameCounter;

    // RHI设备状态, 独立于使用的底层OpenGL上下文.
    FOpenGLRHIState                        PendingState;
    FOpenGLStreamedVertexBufferArray    DynamicVertexBuffers;
    FOpenGLStreamedIndexBufferArray        DynamicIndexBuffers;
    FSamplerStateRHIRef                    PointSamplerState;

    // 已创建的视口.
    TArray<FOpenGLViewport*> Viewports;
    TRefCountPtr<FOpenGLViewport>        DrawingViewport;
    bool                                bRevertToSharedContextAfterDrawingViewport;

    // 已绑定的着色器状态历史.
    TGlobalResource< TBoundShaderStateHistory<10000> > BoundShaderStateHistory;

    // 逐上下文状态缓存.
    FOpenGLContextState InvalidContextState;
    FOpenGLContextState    SharedContextState;
    FOpenGLContextState    RenderingContextState;

    // 统一缓冲区.
    TArray<FRHIUniformBuffer*> GlobalUniformBuffers;
    TMap<GLuint, TPair<GLenum, GLenum>> TextureMipLimits;

    // 底层平台相关的数据.
    FPlatformOpenGLDevice* PlatformDevice;

    // 查询相关.
    TArray<FOpenGLRenderQuery*> Queries;
    FCriticalSection QueriesListCriticalSection;
    
    // 配置和呈现数据.
    FOpenGLGPUProfiler GPUProfilingData;
    FCriticalSection CustomPresentSection;
    TRefCountPtr<class FRHICustomPresent> CustomPresent;
    
    (......)
};

// Engine\Source\Runtime\RHI\Public\RHIValidation.h

class FValidationRHI : public FDynamicRHI
{
public:
    RHI_API FValidationRHI(FDynamicRHI* InRHI);
    RHI_API virtual ~FValidationRHI();

    virtual FSamplerStateRHIRef RHICreateSamplerState(const FSamplerStateInitializerRHI& Initializer) override final;
    virtual FRasterizerStateRHIRef RHICreateRasterizerState(const FRasterizerStateInitializerRHI& Initializer) override final;
    virtual FDepthStencilStateRHIRef RHICreateDepthStencilState(const FDepthStencilStateInitializerRHI& Initializer) override final;
    
    (......)
    
    // RHI实例.
    FDynamicRHI*    RHI;
    // 所属的上下文.
    TIndirectArray<IRHIComputeContext> OwnedContexts;
    // 深度模板状态列表.
    TMap<FRHIDepthStencilState*, FDepthStencilStateInitializerRHI> DepthStencilStates;
};

// Engine\Source\Runtime\VulkanRHI\Public\VulkanDynamicRHI.h

class FVulkanDynamicRHI : public FDynamicRHI
{
public:
    FVulkanDynamicRHI();
    ~FVulkanDynamicRHI();

    // FDynamicRHI interface.
    virtual void Init() final override;
    virtual void PostInit() final override;
    virtual void Shutdown() final override;;
    virtual const TCHAR* GetName() final override { return TEXT("Vulkan"); }

    void InitInstance();

    virtual FSamplerStateRHIRef RHICreateSamplerState(const FSamplerStateInitializerRHI& Initializer) final override;
    virtual FRasterizerStateRHIRef RHICreateRasterizerState(const FRasterizerStateInitializerRHI& Initializer) final override;
    virtual FDepthStencilStateRHIRef RHICreateDepthStencilState(const FDepthStencilStateInitializerRHI& Initializer) final override;
    
    (......)
    
protected:
    // 实例.
    VkInstance Instance;
    TArray<const ANSICHAR*> InstanceExtensions;
    TArray<const ANSICHAR*> InstanceLayers;

    // 设备.
    TArray<FVulkanDevice*> Devices;
    FVulkanDevice* Device;

    // 视口.
    TArray<FVulkanViewport*> Viewports;
    TRefCountPtr<FVulkanViewport> DrawingViewport;

    // 缓存.
    IConsoleObject* SavePipelineCacheCmd = nullptr;
    IConsoleObject* RebuildPipelineCacheCmd = nullptr;

    // 临界区.
    FCriticalSection LockBufferCS;

    // 内部接口.
    void CreateInstance();
    void SelectAndInitDevice();
    void InitGPU(FVulkanDevice* Device);
    void InitDevice(FVulkanDevice* Device);
    
    (......)
};

// Engine\Source\Runtime\Windows\D3D11RHI\Private\D3D11RHIPrivate.h

class D3D11RHI_API FD3D11DynamicRHI : public FDynamicRHI, public IRHICommandContextPSOFallback
{
public:
    FD3D11DynamicRHI(IDXGIFactory1* InDXGIFactory1,D3D_FEATURE_LEVEL InFeatureLevel,int32 InChosenAdapter, const DXGI_ADAPTER_DESC& ChosenDescription);
    virtual ~FD3D11DynamicRHI();

    virtual void InitD3DDevice();

    // FDynamicRHI interface.
    virtual void Init() override;
    virtual void PostInit() override;
    virtual void Shutdown() override;
    virtual const TCHAR* GetName() override { return TEXT("D3D11"); }

    // HDR display output
    virtual void EnableHDR();
    virtual void ShutdownHDR();

    virtual FSamplerStateRHIRef RHICreateSamplerState(const FSamplerStateInitializerRHI& Initializer) final override;
    virtual FRasterizerStateRHIRef RHICreateRasterizerState(const FRasterizerStateInitializerRHI& Initializer) final override;
    virtual FDepthStencilStateRHIRef RHICreateDepthStencilState(const FDepthStencilStateInitializerRHI& Initializer) final override;
    
    (......)

    ID3D11Device* GetDevice() const
    {
        return Direct3DDevice;
    }
    FD3D11DeviceContext* GetDeviceContext() const
    {
        return Direct3DDeviceIMContext;
    }
    IDXGIFactory1* GetFactory() const
    {
        return DXGIFactory1;
    }
    
protected:
    // D3D工厂(接口).
    TRefCountPtr<IDXGIFactory1> DXGIFactory1;
     // D3D设备.
    TRefCountPtr<FD3D11Device> Direct3DDevice;
    // D3D设备的立即上下文.
    TRefCountPtr<FD3D11DeviceContext> Direct3DDeviceIMContext;

    // 线程锁.
    FD3D11LockTracker LockTracker;
    FCriticalSection LockTrackerCS;

    // 视口.
    TArray<FD3D11Viewport*> Viewports;
    TRefCountPtr<FD3D11Viewport> DrawingViewport;

    // AMD AGS工具库上下文.
    AGSContext* AmdAgsContext;

    // RT, UAV, 着色器等资源.
    TRefCountPtr<ID3D11RenderTargetView> CurrentRenderTargets[D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT];
    TRefCountPtr<FD3D11UnorderedAccessView> CurrentUAVs[D3D11_PS_CS_UAV_REGISTER_COUNT];
    ID3D11UnorderedAccessView* UAVBound[D3D11_PS_CS_UAV_REGISTER_COUNT];
    TRefCountPtr<ID3D11DepthStencilView> CurrentDepthStencilTarget;
    TRefCountPtr<FD3D11TextureBase> CurrentDepthTexture;
    FD3D11BaseShaderResource* CurrentResourcesBoundAsSRVs[SF_NumStandardFrequencies][D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT];
    FD3D11BaseShaderResource* CurrentResourcesBoundAsVBs[D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT];
    FD3D11BaseShaderResource* CurrentResourceBoundAsIB;
    int32 MaxBoundShaderResourcesIndex[SF_NumStandardFrequencies];
    FUniformBufferRHIRef BoundUniformBuffers[SF_NumStandardFrequencies][MAX_UNIFORM_BUFFERS_PER_SHADER_STAGE];
    uint16 DirtyUniformBuffers[SF_NumStandardFrequencies];
    TArray<FRHIUniformBuffer*> GlobalUniformBuffers;

    // 已创建的常量缓冲区.
    TArray<TRefCountPtr<FD3D11ConstantBuffer> > VSConstantBuffers;
    TArray<TRefCountPtr<FD3D11ConstantBuffer> > HSConstantBuffers;
    TArray<TRefCountPtr<FD3D11ConstantBuffer> > DSConstantBuffers;
    TArray<TRefCountPtr<FD3D11ConstantBuffer> > PSConstantBuffers;
    TArray<TRefCountPtr<FD3D11ConstantBuffer> > GSConstantBuffers;
    TArray<TRefCountPtr<FD3D11ConstantBuffer> > CSConstantBuffers;

    // 已绑定的着色器状态历史.
    TGlobalResource< TBoundShaderStateHistory<10000> > BoundShaderStateHistory;
    FComputeShaderRHIRef CurrentComputeShader;

    (......)
};

它们的核心继承UML图如下:

需要注意的是,传统图形API(D3D11、OpenGL)除了继承FDynamicRHI,还需要继承IRHICommandContextPSOFallback,因为需要借助后者的接口处理PSO的数据和行为,以保证传统和现代API对PSO的一致处理行为。

也正因为此,现代图形API(D3D12、Vulkan、Metal)不需要继承IRHICommandContext的任何继承体系的类型,单单直接继承FDynamicRHI就可以处理RHI层的所有数据和操作。

既然现代图形API(D3D12、Vulkan、Metal)的DynamicRHI没有继承IRHICommandContext的任何继承体系的类型,那么它们是如何实现FDynamicRHI::RHIGetDefaultContext的接口?

下面以FD3D12DynamicRHI为例:

IRHICommandContext* FD3D12DynamicRHI::RHIGetDefaultContext()
{
    FD3D12Adapter& Adapter = GetAdapter();

    IRHICommandContext* DefaultCommandContext = nullptr;    
    if (GNumExplicitGPUsForRendering > 1) // 多GPU
    {
        DefaultCommandContext = static_cast<IRHICommandContext*>(&Adapter.GetDefaultContextRedirector());
    }
    else // 单GPU
    {
        FD3D12Device* Device = Adapter.GetDevice(0);
        DefaultCommandContext = static_cast<IRHICommandContext*>(&Device->GetDefaultCommandContext());
    }

    return DefaultCommandContext;
}

 

无论是单GPU还是多GPU,都是从FD3D12CommandContext强制转换而来,而FD3D12CommandContext又是IRHICommandContext的子子子类,因此静态类型转换完全没问题。

 

FD3D11DynamicRHI

FD3D11DynamicRHI包含或引用了若干D3D11平台相关的核心类型,它们的定义如下所示:

// Engine\Source\Runtime\Windows\D3D11RHI\Private\D3D11RHIPrivate.h

class D3D11RHI_API FD3D11DynamicRHI : public FDynamicRHI, public IRHICommandContextPSOFallback
{
    (......)

protected:
    // D3D工厂(接口).
    TRefCountPtr<IDXGIFactory1> DXGIFactory1;
     // D3D设备.
    TRefCountPtr<FD3D11Device> Direct3DDevice;
    // D3D设备的立即上下文.
    TRefCountPtr<FD3D11DeviceContext> Direct3DDeviceIMContext;

    // 视口.
    TArray<FD3D11Viewport*> Viewports;
    TRefCountPtr<FD3D11Viewport> DrawingViewport;

    // AMD AGS工具库上下文.
    AGSContext* AmdAgsContext;

    (......)
};

// Engine\Source\Runtime\Windows\D3D11RHI\Private\Windows\D3D11RHIBasePrivate.h

typedef ID3D11DeviceContext FD3D11DeviceContext;
typedef ID3D11Device FD3D11Device;

// Engine\Source\Runtime\Windows\D3D11RHI\Public\D3D11Viewport.h

class FD3D11Viewport : public FRHIViewport
{
public:
    FD3D11Viewport(class FD3D11DynamicRHI* InD3DRHI) : D3DRHI(InD3DRHI), PresentFailCount(0), ValidState (0), FrameSyncEvent(InD3DRHI);
    FD3D11Viewport(class FD3D11DynamicRHI* InD3DRHI, HWND InWindowHandle, uint32 InSizeX, uint32 InSizeY, bool bInIsFullscreen, EPixelFormat InPreferredPixelFormat);
    ~FD3D11Viewport();

    virtual void Resize(uint32 InSizeX, uint32 InSizeY, bool bInIsFullscreen, EPixelFormat PreferredPixelFormat);
    void ConditionalResetSwapChain(bool bIgnoreFocus);
    void CheckHDRMonitorStatus();

    // 呈现交换链.
    bool Present(bool bLockToVsync);

    // Accessors.
    FIntPoint GetSizeXY() const;
    FD3D11Texture2D* GetBackBuffer() const;
    EColorSpaceAndEOTF GetPixelColorSpace() const;

    void WaitForFrameEventCompletion();
    void IssueFrameEvent()

    IDXGISwapChain* GetSwapChain() const;
    virtual void* GetNativeSwapChain() const override;
    virtual void* GetNativeBackBufferTexture() const override;
    virtual void* GetNativeBackBufferRT() const overrid;

    virtual void SetCustomPresent(FRHICustomPresent* InCustomPresent) override
    virtual FRHICustomPresent* GetCustomPresent() const;

    virtual void* GetNativeWindow(void** AddParam = nullptr) const override;
    static FD3D11Texture2D* GetSwapChainSurface(FD3D11DynamicRHI* D3DRHI, EPixelFormat PixelFormat, uint32 SizeX, uint32 SizeY, IDXGISwapChain* SwapChain);

protected:
    // 动态RHI.
    FD3D11DynamicRHI* D3DRHI;
    // 交换链.
    TRefCountPtr<IDXGISwapChain> SwapChain;
    // 后渲染缓冲.
    TRefCountPtr<FD3D11Texture2D> BackBuffer;

    FD3D11EventQuery FrameSyncEvent;
    FCustomPresentRHIRef CustomPresent;

    (......)
};

FD3D11DynamicRHI绘制成UML图之后如下所示:

 

 

FOpenGLDynamicRHI

FOpenGLDynamicRHI相关的核心类型定义如下:

class OPENGLDRV_API FOpenGLDynamicRHI  final : public FDynamicRHI, public IRHICommandContextPSOFallback
{
    (......)
    
private:
    // 已创建的视口.
    TArray<FOpenGLViewport*> Viewports;
    // 底层平台相关的数据.
    FPlatformOpenGLDevice* PlatformDevice;
};

// Engine\Source\Runtime\OpenGLDrv\Public\OpenGLResources.h

class FOpenGLViewport : public FRHIViewport
{
public:
    FOpenGLViewport(class FOpenGLDynamicRHI* InOpenGLRHI,void* InWindowHandle,uint32 InSizeX,uint32 InSizeY,bool bInIsFullscreen,EPixelFormat PreferredPixelFormat);
    ~FOpenGLViewport();

    void Resize(uint32 InSizeX,uint32 InSizeY,bool bInIsFullscreen);

    // Accessors.
    FIntPoint GetSizeXY() const;
    FOpenGLTexture2D *GetBackBuffer() const;
    bool IsFullscreen( void ) const;

    void WaitForFrameEventCompletion();
    void IssueFrameEvent();
    virtual void* GetNativeWindow(void** AddParam) const override;

    struct FPlatformOpenGLContext* GetGLContext() const;
    FOpenGLDynamicRHI* GetOpenGLRHI() const;

    virtual void SetCustomPresent(FRHICustomPresent* InCustomPresent) override;
    FRHICustomPresent* GetCustomPresent() const;
    
private:
    FOpenGLDynamicRHI* OpenGLRHI;
    struct FPlatformOpenGLContext* OpenGLContext;
    uint32 SizeX;
    uint32 SizeY;
    bool bIsFullscreen;
    EPixelFormat PixelFormat;
    bool bIsValid;
    TRefCountPtr<FOpenGLTexture2D> BackBuffer;
    FOpenGLEventQuery FrameSyncEvent;
    FCustomPresentRHIRef CustomPresent;
};

// Engine\Source\Runtime\OpenGLDrv\Private\Android\AndroidOpenGL.cpp

// 安卓系统的OpenGL设备.
struct FPlatformOpenGLDevice
{
    bool TargetDirty;

    void SetCurrentSharedContext();
    void SetCurrentRenderingContext();
    void SetupCurrentContext();
    void SetCurrentNULLContext();

    FPlatformOpenGLDevice();
    ~FPlatformOpenGLDevice();
    
    void Init();
    void LoadEXT();
    void Terminate();
    void ReInit();
};

// Engine\Source\Runtime\OpenGLDrv\Private\Windows\OpenGLWindows.cpp

// Windows系统的OpenGL设备.
struct FPlatformOpenGLDevice
{
    FPlatformOpenGLContext    SharedContext;
    FPlatformOpenGLContext    RenderingContext;
    TArray<FPlatformOpenGLContext*>    ViewportContexts;
    bool                    TargetDirty;

    /** Guards against operating on viewport contexts from more than one thread at the same time. */
    FCriticalSection*        ContextUsageGuard;
};

// Engine\Source\Runtime\OpenGLDrv\Private\Lumin\LuminOpenGL.cpp

// Lumin系统的OpenGL设备.
struct FPlatformOpenGLDevice
{
    void SetCurrentSharedContext();
    void SetCurrentRenderingContext();
    void SetCurrentNULLContext();

    FPlatformOpenGLDevice();
    ~FPlatformOpenGLDevice();
    
    void Init();
    void LoadEXT();
    void Terminate();
    void ReInit();
};

// Engine\Source\Runtime\OpenGLDrv\Private\Linux\OpenGLLinux.cpp

// Linux系统的OpenGL设备.
struct FPlatformOpenGLDevice
{
    FPlatformOpenGLContext    SharedContext;
    FPlatformOpenGLContext    RenderingContext;
    int32                    NumUsedContexts;
    FCriticalSection*        ContextUsageGuard;
};

// Engine\Source\Runtime\OpenGLDrv\Private\Lumin\LuminGL4.cpp

// Lumin系统的OpenGL设备.
struct FPlatformOpenGLDevice
{
    FPlatformOpenGLContext    SharedContext;
    FPlatformOpenGLContext    RenderingContext;
    TArray<FPlatformOpenGLContext*>    ViewportContexts;
    bool                    TargetDirty;
    FCriticalSection*        ContextUsageGuard;
};
以上显示不同操作系统,OpenGL设备对象的定义有所不同。实际上,OpenGL上下文也因操作系统而异,下面以Windows为例:

// Engine\Source\Runtime\OpenGLDrv\Private\Windows\OpenGLWindows.cpp

struct FPlatformOpenGLContext
{
    // 窗口句柄
    HWND WindowHandle;
    // 设备上下文.
    HDC DeviceContext;
    // OpenGL上下文.
    HGLRC OpenGLContext;
    
    // 其它实际.
    bool bReleaseWindowOnDestroy;
    int32 SyncInterval;
    GLuint    ViewportFramebuffer;
    GLuint    VertexArrayObject;    // one has to be generated and set for each context (OpenGL 3.2 Core requirements)
    GLuint    BackBufferResource;
    GLenum    BackBufferTarget;
};

 

FOpenGLDynamicRHI绘制成的UML图如下所示:

 

FD3D12DynamicRHI

FD3D12DynamicRHI的核心类型定义如下:

// Engine\Source\Runtime\D3D12RHI\Private\D3D12RHIPrivate.h

class FD3D12DynamicRHI : public FDynamicRHI
{
    (......)
    
protected:
    // 已选择的适配器.
    TArray<TSharedPtr<FD3D12Adapter>> ChosenAdapters;

    // D3D12设备.
    inline FD3D12Device* GetRHIDevice(uint32 GPUIndex)
    {
        return GetAdapter().GetDevice(GPUIndex);
    }
    
    (......)
};

// Engine\Source\Runtime\D3D12RHI\Private\D3D12Adapter.h

class FD3D12Adapter : public FNoncopyable
{
public:
    void Initialize(FD3D12DynamicRHI* RHI);
    void InitializeDevices();
    void InitializeRayTracing();
    
    // 资源创建.
    HRESULT CreateCommittedResource(...)
    HRESULT CreateBuffer(...);
    template <typename BufferType> 
    BufferType* CreateRHIBuffer(...);

    inline FD3D12CommandContextRedirector& GetDefaultContextRedirector();
    inline FD3D12CommandContextRedirector& GetDefaultAsyncComputeContextRedirector();
    FD3D12FastConstantAllocator& GetTransientUniformBufferAllocator();

    void BlockUntilIdle();
    
    (......)

protected:
    virtual void CreateRootDevice(bool bWithDebug);

    FD3D12DynamicRHI* OwningRHI;

    // LDA设置拥有一个ID3D12Device
    TRefCountPtr<ID3D12Device> RootDevice;
    TRefCountPtr<ID3D12Device1> RootDevice1;
    
    TRefCountPtr<IDXGIAdapter> DxgiAdapter;
    
    TRefCountPtr<IDXGIFactory> DxgiFactory;
    TRefCountPtr<IDXGIFactory2> DxgiFactory2;
    
    // 每个设备代表一个物理GPU“节点”.
    FD3D12Device* Devices[MAX_NUM_GPUS];
    
    FD3D12CommandContextRedirector DefaultContextRedirector;
    FD3D12CommandContextRedirector DefaultAsyncComputeContextRedirector;
    
    TArray<FD3D12Viewport*> Viewports;
    TRefCountPtr<FD3D12Viewport> DrawingViewport;

    (......)
};

// Engine\Source\Runtime\D3D12RHI\Private\D3D12RHICommon.h

class FD3D12AdapterChild
{
protected:
    FD3D12Adapter* ParentAdapter;

    (......)
};

class FD3D12DeviceChild
{
protected:
    FD3D12Device* Parent;
    
    (......)
};

// Engine\Source\Runtime\D3D12RHI\Private\D3D12Device.h

class FD3D12Device : public FD3D12SingleNodeGPUObject, public FNoncopyable, public FD3D12AdapterChild
{
public:
    TArray<FD3D12CommandListHandle> PendingCommandLists;
    
    void Initialize();
    void CreateCommandContexts();
    void InitPlatformSpecific();
    virtual void Cleanup();
    bool GetQueryData(FD3D12RenderQuery& Query, bool bWait);

    ID3D12Device* GetDevice();

    void BlockUntilIdle();
    bool IsGPUIdle();

    FD3D12SamplerState* CreateSampler(const FSamplerStateInitializerRHI& Initializer);

    (......)
    
protected:
    // CommandListManager
    FD3D12CommandListManager* CommandListManager;
    FD3D12CommandListManager* CopyCommandListManager;
    FD3D12CommandListManager* AsyncCommandListManager;
    FD3D12CommandAllocatorManager TextureStreamingCommandAllocatorManager;

    // Allocator
    FD3D12OfflineDescriptorManager RTVAllocator;
    FD3D12OfflineDescriptorManager DSVAllocator;
    FD3D12OfflineDescriptorManager SRVAllocator;
    FD3D12OfflineDescriptorManager UAVAllocator;
    FD3D12DefaultBufferAllocator DefaultBufferAllocator;

    // FD3D12CommandContext
    TArray<FD3D12CommandContext*> CommandContextArray;
    TArray<FD3D12CommandContext*> FreeCommandContexts;
    TArray<FD3D12CommandContext*> AsyncComputeContextArray;

    (......)
};

// Engine\Source\Runtime\D3D12RHI\Public\D3D12Viewport.h

class FD3D12Viewport : public FRHIViewport, public FD3D12AdapterChild
{
public:
    void Init();
    void Resize(uint32 InSizeX, uint32 InSizeY, bool bInIsFullscreen, EPixelFormat PreferredPixelFormat);

    void ConditionalResetSwapChain(bool bIgnoreFocus);
    bool Present(bool bLockToVsync);

    void WaitForFrameEventCompletion();
    bool CurrentOutputSupportsHDR() const;

    (......)
    
private:
    HWND WindowHandle;

#if D3D12_VIEWPORT_EXPOSES_SWAP_CHAIN
    TRefCountPtr<IDXGISwapChain1> SwapChain1;
    TRefCountPtr<IDXGISwapChain4> SwapChain4;
#endif

    TArray<TRefCountPtr<FD3D12Texture2D>> BackBuffers;
    TRefCountPtr<FD3D12Texture2D> DummyBackBuffer_RenderThread;
    uint32 CurrentBackBufferIndex_RHIThread;
    FD3D12Texture2D* BackBuffer_RHIThread;
    TArray<TRefCountPtr<FD3D12Texture2D>> SDRBackBuffers;
    TRefCountPtr<FD3D12Texture2D> SDRDummyBackBuffer_RenderThread;
    FD3D12Texture2D* SDRBackBuffer_RHIThread;

    bool CheckHDRSupport();
    void EnableHDR();
    void ShutdownHDR();
    
    (......)
};

// Engine\Source\Runtime\D3D12RHI\Private\D3D12CommandContext.h

class FD3D12CommandContextBase : public IRHICommandContext, public FD3D12AdapterChild
{
public:
    FD3D12CommandContextBase(class FD3D12Adapter* InParent, FRHIGPUMask InGPUMask, bool InIsDefaultContext, bool InIsAsyncComputeContext);

    void RHIBeginDrawingViewport(FRHIViewport* Viewport, FRHITexture* RenderTargetRHI) final override;
    void RHIEndDrawingViewport(FRHIViewport* Viewport, bool bPresent, bool bLockToVsync) final override;
    void RHIBeginFrame() final override;
    void RHIEndFrame() final override;

    (......)

protected:
    virtual FD3D12CommandContext* GetContext(uint32 InGPUIndex) = 0;

    FRHIGPUMask GPUMask;
    
    (......)
};

class FD3D12CommandContext : public FD3D12CommandContextBase, public FD3D12DeviceChild
{
public:
    FD3D12CommandContext(class FD3D12Device* InParent, bool InIsDefaultContext, bool InIsAsyncComputeContext);
    virtual ~FD3D12CommandContext();

    void EndFrame();
    void ConditionalObtainCommandAllocator();
    void ReleaseCommandAllocator();

    FD3D12CommandListManager& GetCommandListManager();
    void OpenCommandList();
    void CloseCommandList();

    FD3D12CommandListHandle FlushCommands(bool WaitForCompletion = false, EFlushCommandsExtraAction ExtraAction = FCEA_None);
    void Finish(TArray<FD3D12CommandListHandle>& CommandLists);

    FD3D12FastConstantAllocator ConstantsAllocator;
    FD3D12CommandListHandle CommandListHandle;
    FD3D12CommandAllocator* CommandAllocator;
    FD3D12CommandAllocatorManager CommandAllocatorManager;

    FD3D12DynamicRHI& OwningRHI;

    // State Block.
    FD3D12RenderTargetView* CurrentRenderTargets[D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT];
    FD3D12DepthStencilView* CurrentDepthStencilTarget;
    FD3D12TextureBase* CurrentDepthTexture;
    uint32 NumSimultaneousRenderTargets;

    // Uniform Buffer.
    FD3D12UniformBuffer* BoundUniformBuffers[SF_NumStandardFrequencies][MAX_CBS];
    FUniformBufferRHIRef BoundUniformBufferRefs[SF_NumStandardFrequencies][MAX_CBS];
    uint16 DirtyUniformBuffers[SF_NumStandardFrequencies];

    // 常量缓冲区.
    FD3D12ConstantBuffer VSConstantBuffer;
    FD3D12ConstantBuffer HSConstantBuffer;
    FD3D12ConstantBuffer DSConstantBuffer;
    FD3D12ConstantBuffer PSConstantBuffer;
    FD3D12ConstantBuffer GSConstantBuffer;
    FD3D12ConstantBuffer CSConstantBuffer;

    template <class ShaderType> void SetResourcesFromTables(const ShaderType* RESTRICT);
    template <class ShaderType> uint32 SetUAVPSResourcesFromTables(const ShaderType* RESTRICT Shader);
    void CommitGraphicsResourceTables();
    void CommitComputeResourceTables(FD3D12ComputeShader* ComputeShader);
    void ValidateExclusiveDepthStencilAccess(FExclusiveDepthStencil Src) const;
    void CommitRenderTargetsAndUAVs();

    virtual void SetDepthBounds(float MinDepth, float MaxDepth);
    virtual void SetShadingRate(EVRSShadingRate ShadingRate, EVRSRateCombiner Combiner);

    (......)

protected:
    FD3D12CommandContext* GetContext(uint32 InGPUIndex) final override;
    TArray<FRHIUniformBuffer*> GlobalUniformBuffers;
};

class FD3D12CommandContextRedirector final : public FD3D12CommandContextBase
{
public:
    FD3D12CommandContextRedirector(class FD3D12Adapter* InParent, bool InIsDefaultContext, bool InIsAsyncComputeContext);

    virtual void RHISetComputeShader(FRHIComputeShader* ComputeShader) final override;
    virtual void RHISetComputePipelineState(FRHIComputePipelineState* ComputePipelineState) final override;
    virtual void RHIDispatchComputeShader(uint32 ThreadGroupCountX, uint32 ThreadGroupCountY, uint32 ThreadGroupCountZ) final override;
    
    (......)
    
private:
    FRHIGPUMask PhysicalGPUMask;
    FD3D12CommandContext* PhysicalContexts[MAX_NUM_GPUS];
};

// Engine\Source\Runtime\D3D12RHI\Private\D3D12CommandContext.cpp

class FD3D12CommandContextContainer : public IRHICommandContextContainer
{
    FD3D12Adapter* Adapter;
    FD3D12CommandContext* CmdContext;
    FD3D12CommandContextRedirector* CmdContextRedirector;
    FRHIGPUMask GPUMask;
    TArray<FD3D12CommandListHandle> CommandLists;

    (......)
};

以上可知,D3D12涉及的核心类型非常多,涉及多层级的复杂的数据结构链,其内存布局如下所示:

[Engine]--
        |
        |-[RHI]--
                |
                |-[Adapter]-- (LDA)
                |            |
                |            |- [Device]
                |            |
                |            |- [Device]
                |
                |-[Adapter]--
                            |
                            |- [Device]--
                                        |
                                        |-[CommandContext]
                                        |
                                        |-[CommandContext]---
                                                            |
                                                            |-[StateCache]

在这种方案下,FD3D12Device表示1个节点,属于1个物理适配器。这种结构允许一个RHI控制几个不同类型的硬件设置,例如:

  • 单GPU系统(常规案例)。
  • 多GPU系统,如LDA(Crossfire/SLI)。
  • 非对称多GPU系统,如分离、集成GPU协作系统。

将D3D12的核心类抽象成UML图之后,如下所示:

 

FVulkanDynamicRHI

FVulkanDynamicRHI涉及的核心类如下:

// Engine\Source\Runtime\VulkanRHI\Public\VulkanDynamicRHI.h

class FVulkanDynamicRHI : public FDynamicRHI
{
public:
    // FDynamicRHI interface.
    virtual void Init() final override;
    virtual void PostInit() final override;
    virtual void Shutdown() final override;;
    void InitInstance();

    (......)
    
protected:
    // 实例.
    VkInstance Instance;
    
    // 设备.
    TArray<FVulkanDevice*> Devices;
    FVulkanDevice* Device;

    // 视口.
    TArray<FVulkanViewport*> Viewports;
    
    (......)
};

// Engine\Source\Runtime\VulkanRHI\Private\VulkanDevice.h

class FVulkanDevice
{
public:
    FVulkanDevice(FVulkanDynamicRHI* InRHI, VkPhysicalDevice Gpu);
    ~FVulkanDevice();

    bool QueryGPU(int32 DeviceIndex);
    void InitGPU(int32 DeviceIndex);
    void CreateDevice();
    void PrepareForDestroy();
    void Destroy();

    void WaitUntilIdle();
    void PrepareForCPURead();
    void SubmitCommandsAndFlushGPU();

    (......)
    
private:
    void SubmitCommands(FVulkanCommandListContext* Context);

    // vk设备.
    VkDevice Device;
    // vk物理设备.
    VkPhysicalDevice Gpu;
    
    VkPhysicalDeviceProperties GpuProps;
    VkPhysicalDeviceFeatures PhysicalFeatures;

    // 管理器.
    VulkanRHI::FDeviceMemoryManager DeviceMemoryManager;
    VulkanRHI::FMemoryManager MemoryManager;
    VulkanRHI::FDeferredDeletionQueue2 DeferredDeletionQueue;
    VulkanRHI::FStagingManager StagingManager;
    VulkanRHI::FFenceManager FenceManager;
    FVulkanDescriptorPoolsManager* DescriptorPoolsManager = nullptr;
    
    FVulkanDescriptorSetCache* DescriptorSetCache = nullptr;
    FVulkanShaderFactory ShaderFactory;

    // 队列.
    FVulkanQueue* GfxQueue;
    FVulkanQueue* ComputeQueue;
    FVulkanQueue* TransferQueue;
    FVulkanQueue* PresentQueue;

    // GPU品牌.
    EGpuVendorId VendorId = EGpuVendorId::NotQueried;

    // 命令队列上下文.
    FVulkanCommandListContextImmediate* ImmediateContext;
    FVulkanCommandListContext* ComputeContext;
    TArray<FVulkanCommandListContext*> CommandContexts;

    FVulkanDynamicRHI* RHI = nullptr;
    class FVulkanPipelineStateCacheManager* PipelineStateCache;
    
    (......)
};

// Engine\Source\Runtime\VulkanRHI\Private\VulkanQueue.h

class FVulkanQueue
{
public:
    FVulkanQueue(FVulkanDevice* InDevice, uint32 InFamilyIndex);
    ~FVulkanQueue();

    void Submit(FVulkanCmdBuffer* CmdBuffer, uint32 NumSignalSemaphores = 0, VkSemaphore* SignalSemaphores = nullptr);
    void Submit(FVulkanCmdBuffer* CmdBuffer, VkSemaphore SignalSemaphore);

    void GetLastSubmittedInfo(FVulkanCmdBuffer*& OutCmdBuffer, uint64& OutFenceCounter) const;

    (......)
    
private:
    // vk队列
    VkQueue Queue;
    // 家族索引.
    uint32 FamilyIndex;
    // 队列索引.
    uint32 QueueIndex;
    FVulkanDevice* Device;

    // vk命令缓冲.
    FVulkanCmdBuffer* LastSubmittedCmdBuffer;
    uint64 LastSubmittedCmdBufferFenceCounter;
    uint64 SubmitCounter;
    mutable FCriticalSection CS;

    void UpdateLastSubmittedCommandBuffer(FVulkanCmdBuffer* CmdBuffer);
};

// Engine\Source\Runtime\VulkanRHI\Public\VulkanMemory.h

// 设备子节点.
class FDeviceChild
{
public:
    FDeviceChild(FVulkanDevice* InDevice = nullptr);
    
    (......)
    
 protected:
    FVulkanDevice* Device;
};

// Engine\Source\Runtime\VulkanRHI\Private\VulkanContext.h

class FVulkanCommandListContext : public IRHICommandContext
{
public:
    FVulkanCommandListContext(FVulkanDynamicRHI* InRHI, FVulkanDevice* InDevice, FVulkanQueue* InQueue, FVulkanCommandListContext* InImmediate);
    virtual ~FVulkanCommandListContext();

    static inline FVulkanCommandListContext& GetVulkanContext(IRHICommandContext& CmdContext);

    inline bool IsImmediate() const;

    virtual void RHISetStreamSource(uint32 StreamIndex, FRHIVertexBuffer* VertexBuffer, uint32 Offset) final override;
    virtual void RHISetViewport(float MinX, float MinY, float MinZ, float MaxX, float MaxY, float MaxZ) final override;
    virtual void RHISetScissorRect(bool bEnable, uint32 MinX, uint32 MinY, uint32 MaxX, uint32 MaxY) final override;
    
    (......)

    inline FVulkanDevice* GetDevice() const;
    void PrepareParallelFromBase(const FVulkanCommandListContext& BaseContext);

protected:
    FVulkanDynamicRHI* RHI;
    FVulkanCommandListContext* Immediate;
    FVulkanDevice* Device;
    FVulkanQueue* Queue;
    
    FVulkanUniformBufferUploader* UniformBufferUploader;
    FVulkanCommandBufferManager* CommandBufferManager;
    static FVulkanLayoutManager LayoutManager;

private:
    FVulkanGPUProfiler GpuProfiler;
    TArray<FRHIUniformBuffer*> GlobalUniformBuffers;
    
    (......)
};

// 立即模式的命令队列上下文.
class FVulkanCommandListContextImmediate : public FVulkanCommandListContext
{
public:
    FVulkanCommandListContextImmediate(FVulkanDynamicRHI* InRHI, FVulkanDevice* InDevice, FVulkanQueue* InQueue);
};

// 命令上下文容器.
struct FVulkanCommandContextContainer : public IRHICommandContextContainer, public VulkanRHI::FDeviceChild
{
    FVulkanCommandListContext* CmdContext;

    FVulkanCommandContextContainer(FVulkanDevice* InDevice);

    virtual IRHICommandContext* GetContext() override final;
    virtual void FinishContext() override final;
    virtual void SubmitAndFreeContextContainer(int32 Index, int32 Num) override final;
    
    void* operator new(size_t Size);
    void operator delete(void* RawMemory);
    
    (......)
};

// Engine\Source\Runtime\VulkanRHI\Private\VulkanViewport.h

class FVulkanViewport : public FRHIViewport, public VulkanRHI::FDeviceChild
{
public:
    FVulkanViewport(FVulkanDynamicRHI* InRHI, FVulkanDevice* InDevice, void* InWindowHandle, uint32 InSizeX,uint32 InSizeY,bool bInIsFullscreen, EPixelFormat InPreferredPixelFormat);
    ~FVulkanViewport();

    void AdvanceBackBufferFrame(FRHICommandListImmediate& RHICmdList);
    void WaitForFrameEventCompletion();

    virtual void SetCustomPresent(FRHICustomPresent* InCustomPresent) override final;
    virtual FRHICustomPresent* GetCustomPresent() const override final;
    virtual void Tick(float DeltaTime) override final;
    bool Present(FVulkanCommandListContext* Context, FVulkanCmdBuffer* CmdBuffer, FVulkanQueue* Queue, FVulkanQueue* PresentQueue, bool bLockToVsync);

    (......)
    
protected:
    TArray<VkImage, TInlineAllocator<NUM_BUFFERS*2>> BackBufferImages;
    TArray<VulkanRHI::FSemaphore*, TInlineAllocator<NUM_BUFFERS*2>> RenderingDoneSemaphores;
    TArray<FVulkanTextureView, TInlineAllocator<NUM_BUFFERS*2>> TextureViews;
    TRefCountPtr<FVulkanBackBuffer> RHIBackBuffer;
    TRefCountPtr<FVulkanTexture2D>    RenderingBackBuffer;
    
    /** narrow-scoped section that locks access to back buffer during its recreation*/
    FCriticalSection RecreatingSwapchain;

    FVulkanDynamicRHI* RHI;
    FVulkanSwapChain* SwapChain;
    void* WindowHandle;
    VulkanRHI::FSemaphore* AcquiredSemaphore;
    FCustomPresentRHIRef CustomPresent;
    FVulkanCmdBuffer* LastFrameCommandBuffer = nullptr;
    
    (......)
};

若将Vulkan RHI的核心类型绘制成UML图,则是如下图所示:

 

FMetalDynamicRHI

FMetalDynamicRHI的核心类型定义如下:

// Engine\Source\Runtime\Apple\MetalRHI\Private\MetalDynamicRHI.h

class FMetalDynamicRHI : public FDynamicRHI
{
public:
    // FDynamicRHI interface.
    virtual void Init();
    virtual void Shutdown() {}
    
    (......)
    
private:
    // 立即模式上下文.
    FMetalRHIImmediateCommandContext ImmediateContext;
    // 异步计算上下文.
    FMetalRHICommandContext* AsyncComputeContext;
    
    (......)
};

// Engine\Source\Runtime\Apple\MetalRHI\Public\MetalRHIContext.h

class FMetalRHICommandContext : public IRHICommandContext
{
public:
    FMetalRHICommandContext(class FMetalProfiler* InProfiler, FMetalContext* WrapContext);
    virtual ~FMetalRHICommandContext();

    virtual void RHISetComputeShader(FRHIComputeShader* ComputeShader) override;
    virtual void RHISetComputePipelineState(FRHIComputePipelineState* ComputePipelineState) override;
    virtual void RHIDispatchComputeShader(uint32 ThreadGroupCountX, uint32 ThreadGroupCountY, uint32 ThreadGroupCountZ) final override;
    
    (......)

protected:
    // Metal上下文.
    FMetalContext* Context;
    
    TSharedPtr<FMetalCommandBufferFence, ESPMode::ThreadSafe> CommandBufferFence;
    class FMetalProfiler* Profiler;
    FMetalBuffer PendingVertexBuffer;

    TArray<FRHIUniformBuffer*> GlobalUniformBuffers;

    (......)
};

class FMetalRHIComputeContext : public FMetalRHICommandContext
{
public:
    FMetalRHIComputeContext(class FMetalProfiler* InProfiler, FMetalContext* WrapContext);
    virtual ~FMetalRHIComputeContext();
    
    virtual void RHISetAsyncComputeBudget(EAsyncComputeBudget Budget) final override;
    virtual void RHISetComputeShader(FRHIComputeShader* ComputeShader) final override;
    virtual void RHISetComputePipelineState(FRHIComputePipelineState* ComputePipelineState) final override;
    virtual void RHISubmitCommandsHint() final override;
};

class FMetalRHIImmediateCommandContext : public FMetalRHICommandContext
{
public:
    FMetalRHIImmediateCommandContext(class FMetalProfiler* InProfiler, FMetalContext* WrapContext);

    // FRHICommandContext API accessible only on the immediate device context
    virtual void RHIBeginDrawingViewport(FRHIViewport* Viewport, FRHITexture* RenderTargetRHI) final override;
    virtual void RHIEndDrawingViewport(FRHIViewport* Viewport, bool bPresent, bool bLockToVsync) final override;
    
    (......)
};

// Engine\Source\Runtime\Apple\MetalRHI\Private\MetalContext.h

// 上下文.
class FMetalContext
{
public:
    FMetalContext(mtlpp::Device InDevice, FMetalCommandQueue& Queue, bool const bIsImmediate);
    virtual ~FMetalContext();
    
    mtlpp::Device& GetDevice();
    
    bool PrepareToDraw(uint32 PrimitiveType, EMetalIndexType IndexType = EMetalIndexType_None);
    void SetRenderPassInfo(const FRHIRenderPassInfo& RenderTargetsInfo, bool const bRestart = false);

    void SubmitCommandsHint(uint32 const bFlags = EMetalSubmitFlagsCreateCommandBuffer);
    void SubmitCommandBufferAndWait();
    void ResetRenderCommandEncoder();
    
    void DrawPrimitive(uint32 PrimitiveType, uint32 BaseVertexIndex, uint32 NumPrimitives, uint32 NumInstances);
    void DrawPrimitiveIndirect(uint32 PrimitiveType, FMetalVertexBuffer* VertexBuffer, uint32 ArgumentOffset);
    void DrawIndexedPrimitive(FMetalBuffer const& IndexBuffer, ...);
    void DrawIndexedIndirect(FMetalIndexBuffer* IndexBufferRHI, ...);
    void DrawIndexedPrimitiveIndirect(uint32 PrimitiveType, ...);
    void DrawPatches(uint32 PrimitiveType, ...);
    
    (......)

protected:
    // Metal底层设备.
    mtlpp::Device Device;
    
    FMetalCommandQueue& CommandQueue;
    FMetalCommandList CommandList;
    
    FMetalStateCache StateCache;
    FMetalRenderPass RenderPass;
    
    dispatch_semaphore_t CommandBufferSemaphore;
    TSharedPtr<FMetalQueryBufferPool, ESPMode::ThreadSafe> QueryBuffer;
    TRefCountPtr<FMetalFence> StartFence;
    TRefCountPtr<FMetalFence> EndFence;
    
    int32 NumParallelContextsInPass;
    
    (......)
};

// Engine\Source\Runtime\Apple\MetalRHI\Private\MetalCommandQueue.h

class FMetalCommandQueue
{
public:
    FMetalCommandQueue(mtlpp::Device Device, uint32 const MaxNumCommandBuffers = 0);
    ~FMetalCommandQueue(void);
    
    mtlpp::CommandBuffer CreateCommandBuffer(void);
    void CommitCommandBuffer(mtlpp::CommandBuffer& CommandBuffer);
    void SubmitCommandBuffers(TArray<mtlpp::CommandBuffer> BufferList, uint32 Index, uint32 Count);
    FMetalFence* CreateFence(ns::String const& Label) const;
    void GetCommittedCommandBufferFences(TArray<mtlpp::CommandBufferFence>& Fences);
    
    mtlpp::Device& GetDevice(void);
    
    static mtlpp::ResourceOptions GetCompatibleResourceOptions(mtlpp::ResourceOptions Options);
    static inline bool SupportsFeature(EMetalFeatures InFeature);
    static inline bool SupportsSeparateMSAAAndResolveTarget();
    
    (......)

private:
    // 设备.
    mtlpp::Device Device;
    // 命令队列.
    mtlpp::CommandQueue CommandQueue;
    // 命令缓存区列表.(注意是数组的数组)
    TArray<TArray<mtlpp::CommandBuffer>> CommandBuffers;
    
    TLockFreePointerListLIFO<mtlpp::CommandBufferFence> CommandBufferFences;
    uint64 ParallelCommandLists;
};

// Engine\Source\Runtime\Apple\MetalRHI\Private\MetalCommandList.h

class FMetalCommandList
{
public:
    FMetalCommandList(FMetalCommandQueue& InCommandQueue, bool const bInImmediate);
    ~FMetalCommandList(void);
    
    void Commit(mtlpp::CommandBuffer& Buffer, TArray<ns::Object<mtlpp::CommandBufferHandler>> CompletionHandlers, bool const bWait, bool const bIsLastCommandBuffer);
    void Submit(uint32 Index, uint32 Count);
    
    bool IsImmediate(void) const;
    bool IsParallel(void) const;
    void SetParallelIndex(uint32 Index, uint32 Num);
    uint32 GetParallelIndex(void) const;
    uint32 GetParallelNum(void) const;

    (......)
    
private:
    // 所属的FMetalCommandQueue.
    FMetalCommandQueue& CommandQueue;
    // 已提交的命令缓冲列表.
    TArray<mtlpp::CommandBuffer> SubmittedBuffers;
};

 

相比其它现代图形API而言,FMetalDynamicRHI的概念和接口都简介多了。其UML图如下:

 

不同平台加载FDynamicRHI流程

IDynamicRHIModule

IDynamicRHIModule从IModuleInterface派生,可通过FModuleManager::LoadModule()等函数动态地加载,是各个DynamicRHIModule的公共接口类。
FNullDynamicRHIModule、FOpenGLDynamicRHIModule、FD3D11DynamicRHIModule、FD3D12DynamicRHIModule、FVulkanDynamicRHIModule、FMetalDynamicRHIModule、FEmptyDynamicRHIModule通过重写CreateRHI函数,
创建并返回对应的FDynamicRHI类型,相关的代码如下:
// FNullDynamicRHIModule模块
class FNullDynamicRHIModule
    : public IDynamicRHIModule
{
public:

    // IDynamicRHIModule

    virtual bool SupportsDynamicReloading() override { return false; }
    virtual bool IsSupported() override { return true; }

    virtual FDynamicRHI* CreateRHI(ERHIFeatureLevel::Type RequestedFeatureLevel = ERHIFeatureLevel::Num) override
    {
        return new FNullDynamicRHI();
    }
};

// FOpenGLDynamicRHIModule模块
class FOpenGLDynamicRHIModule : public IDynamicRHIModule
{
public:
    
    // IModuleInterface
    virtual bool SupportsDynamicReloading() override { return false; }

    // IDynamicRHIModule
    virtual bool IsSupported() override { return true; }

    virtual FDynamicRHI* CreateRHI(ERHIFeatureLevel::Type RequestedFeatureLevel = ERHIFeatureLevel::Num) override
    {
        GRequestedFeatureLevel = InRequestedFeatureLevel;
        return new FOpenGLDynamicRHI();
    }
};

// FD3D11DynamicRHIModule模块
class FD3D11DynamicRHIModule : public IDynamicRHIModule
{
public:
    // IModuleInterface    
    virtual bool SupportsDynamicReloading() override { return false; }
    virtual void StartupModule() override;

    // IDynamicRHIModule
    virtual bool IsSupported() override
    {
        // if not computed yet
        if(!ChosenAdapter.IsValid())
        {
            FindAdapter();
        }

        // The hardware must support at least 10.0 (usually 11_0, 10_0 or 10_1).
        return ChosenAdapter.IsValid()
            && ChosenAdapter.MaxSupportedFeatureLevel != D3D_FEATURE_LEVEL_9_1
            && ChosenAdapter.MaxSupportedFeatureLevel != D3D_FEATURE_LEVEL_9_2
            && ChosenAdapter.MaxSupportedFeatureLevel != D3D_FEATURE_LEVEL_9_3;
    }
    
    virtual FDynamicRHI* CreateRHI(ERHIFeatureLevel::Type RequestedFeatureLevel = ERHIFeatureLevel::Num) override
    {
        {
#if PLATFORM_HOLOLENS
        GMaxRHIFeatureLevel = ERHIFeatureLevel::ES3_1;
        GMaxRHIShaderPlatform = SP_PCD3D_ES3_1;
#endif

        TRefCountPtr<IDXGIFactory1> DXGIFactory1;
        SafeCreateDXGIFactory(DXGIFactory1.GetInitReference());
        check(DXGIFactory1);

        GD3D11RHI = new FD3D11DynamicRHI(DXGIFactory1,ChosenAdapter.MaxSupportedFeatureLevel,ChosenAdapter.AdapterIndex,ChosenDescription);
        FDynamicRHI* FinalRHI = GD3D11RHI;

#if ENABLE_RHI_VALIDATION
        if (FParse::Param(FCommandLine::Get(), TEXT("RHIValidation")))
        {
            FinalRHI = new FValidationRHI(FinalRHI);
        }
#endif

        return FinalRHI;
}
    }

private:
    FD3D11Adapter ChosenAdapter;
    // we don't use GetDesc().Description as there is a bug with Optimus where it can report the wrong name
    DXGI_ADAPTER_DESC ChosenDescription;

    // set MaxSupportedFeatureLevel and ChosenAdapter
    void FindAdapter();
};

// FD3D12DynamicRHIModule模块
class FD3D12DynamicRHIModule : public IDynamicRHIModule
{
public:

    FD3D12DynamicRHIModule()
    {
    }

    ~FD3D12DynamicRHIModule()
    {
    }

    // IModuleInterface
    virtual bool SupportsDynamicReloading() override { return false; }
    virtual void StartupModule() override;
    virtual void ShutdownModule() override;

    // IDynamicRHIModule
    virtual bool IsSupported() override
    {
    #if !PLATFORM_HOLOLENS
        if (!FPlatformMisc::VerifyWindowsVersion(10, 0))
        {
            return false;
        }
    #endif

        // If not computed yet
        if (ChosenAdapters.Num() == 0)
        {
            FindAdapter();
        }

        // The hardware must support at least 11.0.
        return ChosenAdapters.Num() > 0
            && ChosenAdapters[0]->GetDesc().IsValid()
            && ChosenAdapters[0]->GetDesc().MaxSupportedFeatureLevel >= D3D_FEATURE_LEVEL_11_0;
    }
    
    virtual FDynamicRHI* CreateRHI(ERHIFeatureLevel::Type RequestedFeatureLevel = ERHIFeatureLevel::Num) override
    {
        ERHIFeatureLevel::Type PreviewFeatureLevel;
        if (!GIsEditor && RHIGetPreviewFeatureLevel(PreviewFeatureLevel))
        {
            check(PreviewFeatureLevel == ERHIFeatureLevel::ES3_1);

            // ES3.1 feature level emulation in D3D
            GMaxRHIFeatureLevel = PreviewFeatureLevel;
            if (GMaxRHIFeatureLevel == ERHIFeatureLevel::ES3_1)
            {
                GMaxRHIShaderPlatform = SP_PCD3D_ES3_1;
            }
        }
        else
        {
            GMaxRHIFeatureLevel = ERHIFeatureLevel::SM5;
            GMaxRHIShaderPlatform = SP_PCD3D_SM5;
        }

    #if USE_PIX
        bool bPixEventEnabled = (WindowsPixDllHandle != nullptr);
    #else
        bool bPixEventEnabled = false;
    #endif // USE_PIX

        GD3D12RHI = new FD3D12DynamicRHI(ChosenAdapters, bPixEventEnabled);
    #if ENABLE_RHI_VALIDATION
        if (FParse::Param(FCommandLine::Get(), TEXT("RHIValidation")))
        {
            return new FValidationRHI(GD3D12RHI);
        }
    #endif
        return GD3D12RHI;
    }

private:

#if USE_PIX && (PLATFORM_WINDOWS || PLATFORM_HOLOLENS)
    void* WindowsPixDllHandle = nullptr;
#endif // USE_PIX && (PLATFORM_WINDOWS || PLATFORM_HOLOLENS)

    TArray<TSharedPtr<FD3D12Adapter>> ChosenAdapters;

    // set MaxSupportedFeatureLevel and ChosenAdapter
    void FindAdapter();
};

// FVulkanDynamicRHIModule模块
class FVulkanDynamicRHIModule : public IDynamicRHIModule
{
public:
    // IDynamicRHIModule
    virtual bool IsSupported() override
    {
        return FVulkanPlatform::IsSupported();
    }

    virtual FDynamicRHI* CreateRHI(ERHIFeatureLevel::Type RequestedFeatureLevel = ERHIFeatureLevel::Num) override
    {
        FVulkanPlatform::SetupMaxRHIFeatureLevelAndShaderPlatform(InRequestedFeatureLevel);
        check(GMaxRHIFeatureLevel != ERHIFeatureLevel::Num);

        GVulkanRHI = new FVulkanDynamicRHI();
        FDynamicRHI* FinalRHI = GVulkanRHI;

    #if ENABLE_RHI_VALIDATION
        if (FParse::Param(FCommandLine::Get(), TEXT("RHIValidation")))
        {
            FinalRHI = new FValidationRHI(FinalRHI);
        }
    #endif

        return FinalRHI;
    }
};


// FMetalDynamicRHIModule模块
class FMetalDynamicRHIModule : public IDynamicRHIModule
{
public:
    virtual bool IsSupported() override final { return true; }

    virtual FDynamicRHI* CreateRHI(ERHIFeatureLevel::Type RequestedFeatureLevel = ERHIFeatureLevel::Num) override final
    {
        LLM(MetalLLM::Initialise());
        return new FMetalDynamicRHI(RequestedFeatureLevel);
    }
};

// FEmptyDynamicRHIModule模块
class FEmptyDynamicRHIModule : public IDynamicRHIModule
{
public:
    // IDynamicRHIModule
    virtual bool IsSupported() override { return true; }

    virtual FDynamicRHI* CreateRHI(ERHIFeatureLevel::Type RequestedFeatureLevel = ERHIFeatureLevel::Num) override
    {
        return new FEmptyDynamicRHI();
    }
};

 

DynamicRHIModule的继承关系如下:

 

不同平台实现了各自FDynamicRHI* PlatformCreateDynamicRHI全局函数版本,依据命令行参数或ini配置文件来确定当前应该加载的IDynamicRHIModule,最后调用CreateRHI函数创建出最终使用的FDynamicRHI实例。

并将该实例保存到全局变量extern RHI_API FDynamicRHI* GDynamicRHI中  // UnrealEngine\Engine\Source\Runtime\RHI\Public\DynamicRHI.h

/* UnrealEngine\Engine\Source\Runtime\RHI\Private\Windows\WindowsDynamicRHI.cpp */
// windows平台
FDynamicRHI* PlatformCreateDynamicRHI()
{
    FDynamicRHI* DynamicRHI = nullptr;

#if UE_BUILD_DEBUG || UE_BUILD_DEVELOPMENT
    if (!FPlatformMisc::IsDebuggerPresent())
    {
        if (FParse::Param(FCommandLine::Get(), TEXT("AttachDebugger")))
        {
            // Wait to attach debugger
            do
            {
                FPlatformProcess::Sleep(0);
            }
            while (!FPlatformMisc::IsDebuggerPresent());
        }
    }
#endif

    ERHIFeatureLevel::Type RequestedFeatureLevel;
    const TCHAR* LoadedRHIModuleName;
    IDynamicRHIModule* DynamicRHIModule = LoadDynamicRHIModule(RequestedFeatureLevel, LoadedRHIModuleName);

    if (DynamicRHIModule)
    {
        // Create the dynamic RHI.
        DynamicRHI = DynamicRHIModule->CreateRHI(RequestedFeatureLevel);
        GLoadedRHIModuleName = LoadedRHIModuleName;
    }

    return DynamicRHI;
}


/* UnrealEngine\Engine\Source\Runtime\RHI\Private\Android\AndroidDynamicRHI.cpp */
// Android平台
FDynamicRHI* PlatformCreateDynamicRHI()
{
    FDynamicRHI* DynamicRHI = NULL;

    // Load the dynamic RHI module.
    IDynamicRHIModule* DynamicRHIModule = NULL;
    ERHIFeatureLevel::Type RequestedFeatureLevel = ERHIFeatureLevel::Num;
    FString GraphicsRHI;

    if (FPlatformMisc::ShouldUseVulkan() || FPlatformMisc::ShouldUseDesktopVulkan())
    {
        // Vulkan is required, release the EGL created by FAndroidAppEntry::PlatformInit.
        FAndroidAppEntry::ReleaseEGL();

        DynamicRHIModule = &FModuleManager::LoadModuleChecked<IDynamicRHIModule>(TEXT("VulkanRHI"));
        if (!DynamicRHIModule->IsSupported())
        {
            DynamicRHIModule = &FModuleManager::LoadModuleChecked<IDynamicRHIModule>(TEXT("OpenGLDrv"));
            GraphicsRHI = TEXT("OpenGL");
        }
        else
        {
            RequestedFeatureLevel = FPlatformMisc::ShouldUseDesktopVulkan() ? ERHIFeatureLevel::SM5 : ERHIFeatureLevel::ES3_1;
            GraphicsRHI = TEXT("Vulkan");
        }
    }
    else
    {
        DynamicRHIModule = &FModuleManager::LoadModuleChecked<IDynamicRHIModule>(TEXT("OpenGLDrv"));
        GraphicsRHI = TEXT("OpenGL");
    }

    if (!DynamicRHIModule->IsSupported()) 
    {

    //    FMessageDialog::Open(EAppMsgType::Ok, TEXT("OpenGL 3.2 is required to run the engine."));
        FPlatformMisc::RequestExit(1);
        DynamicRHIModule = NULL;
    }

    if (DynamicRHIModule)
    {
        FApp::SetGraphicsRHI(GraphicsRHI);
        // Create the dynamic RHI.
        DynamicRHI = DynamicRHIModule->CreateRHI(RequestedFeatureLevel);
    }

#if !PLATFORM_LUMIN
    FPlatformMisc::UnlockAndroidWindow();
#endif

    return DynamicRHI;
}

/* UnrealEngine\Engine\Source\Runtime\RHI\Private\Apple\AppleDynamicRHI.cpp */
// macOS、iOS平台
FDynamicRHI* PlatformCreateDynamicRHI()
{
    SCOPED_AUTORELEASE_POOL;

    FDynamicRHI* DynamicRHI = NULL;
    IDynamicRHIModule* DynamicRHIModule = NULL;

    bool const bIsMetalSupported = FPlatformMisc::HasPlatformFeature(TEXT("Metal"));
    
    // Must be Metal!
    if(!bIsMetalSupported)
    {
        FText Title = NSLOCTEXT("AppleDynamicRHI", "OpenGLNotSupportedTitle","Metal Not Supported");
#if PLATFORM_MAC
        FMessageDialog::Open(EAppMsgType::Ok, NSLOCTEXT("MacPlatformCreateDynamicRHI", "OpenGLNotSupported.", "You must have a Metal compatible graphics card and be running Mac OS X 10.11.6 or later to launch this process."), &Title);
#else
        FMessageDialog::Open(EAppMsgType::Ok, NSLOCTEXT("AppleDynamicRHI", "OpenGLNotSupported.", "You must have a Metal compatible iOS or tvOS device with iOS 8 or later to launch this app."), &Title);
#endif
        FPlatformMisc::RequestExit(true);
    }
    
    if (FParse::Param(FCommandLine::Get(),TEXT("opengl")))
    {
        UE_LOG(LogRHI, Log, TEXT("OpenGL command line option ignored; Apple platforms only support Metal."));
    }

    ERHIFeatureLevel::Type RequestedFeatureLevel = ERHIFeatureLevel::Num;
    {
        // Check the list of targeted shader platforms and decide an RHI based off them
        TArray<FString> TargetedShaderFormats;
#if PLATFORM_MAC
        GConfig->GetArray(TEXT("/Script/MacTargetPlatform.MacTargetSettings"), TEXT("TargetedRHIs"), TargetedShaderFormats, GEngineIni);
#else
        bool bSupportsMetalMRT = false;
        GConfig->GetBool(TEXT("/Script/IOSRuntimeSettings.IOSRuntimeSettings"), TEXT("bSupportsMetalMRT"), bSupportsMetalMRT, GEngineIni);
        if (bSupportsMetalMRT)
        {
#if PLATFORM_TVOS
            TargetedShaderFormats.Add(LegacyShaderPlatformToShaderFormat(SP_METAL_MRT_TVOS).ToString());
#else
            TargetedShaderFormats.Add(LegacyShaderPlatformToShaderFormat(SP_METAL_MRT).ToString());
#endif
        }
        
#if PLATFORM_TVOS
        TargetedShaderFormats.Add(LegacyShaderPlatformToShaderFormat(SP_METAL_TVOS).ToString());
#else
        TargetedShaderFormats.Add(LegacyShaderPlatformToShaderFormat(SP_METAL).ToString());
#endif
        
#endif // else branch of PLATFORM_MAC
        
        // Metal is not always available, so don't assume that we can use the first platform
        for (FString Name : TargetedShaderFormats)
        {
            FName ShaderFormatName(*Name);
            EShaderPlatform TargetedPlatform = ShaderFormatToLegacyShaderPlatform(ShaderFormatName);
            
            // Instead use the first platform that *could* work
            if (IsMetalPlatform(TargetedPlatform))
            {
                RequestedFeatureLevel = GetMaxSupportedFeatureLevel(TargetedPlatform);
                break;
            }
        }
    }

    // Load the dynamic RHI module.
    {
        DynamicRHIModule = &FModuleManager::LoadModuleChecked<IDynamicRHIModule>(TEXT("MetalRHI"));
        
        {
#if PLATFORM_MAC
            if (FParse::Param(FCommandLine::Get(),TEXT("metal")))
            {
                RequestedFeatureLevel = ERHIFeatureLevel::SM5;
            }
            else if (FParse::Param(FCommandLine::Get(),TEXT("metalsm5")) || FParse::Param(FCommandLine::Get(),TEXT("metalmrt")))
            {
                RequestedFeatureLevel = ERHIFeatureLevel::SM5;
            }
#else
            if (FParse::Param(FCommandLine::Get(),TEXT("metal")))
            {
                RequestedFeatureLevel = ERHIFeatureLevel::ES3_1;
            }
            else if (FParse::Param(FCommandLine::Get(),TEXT("metalmrt")))
            {
                RequestedFeatureLevel = ERHIFeatureLevel::SM5;
            }
#endif
        }
        FApp::SetGraphicsRHI(TEXT("Metal"));
    }
    
    // Create the dynamic RHI.
    DynamicRHI = DynamicRHIModule->CreateRHI(RequestedFeatureLevel);
    return DynamicRHI;
}

 

在windows平台上,调用堆栈如下:

 

参考

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

 

posted on 2021-12-15 22:58  可可西  阅读(3175)  评论(1编辑  收藏  举报

导航